https://github.com/hnrklssn created 
https://github.com/llvm/llvm-project/pull/93231

The attributes `sized_by`, `counted_by_or_null` and `sized_by_or_null` have 
been added as variants on `counted_by`, each with slightly different semantics. 
 `sized_by` takes a byte size parameter instead of an element count, allowing 
pointees with unknown size. The `counted_by_or_null` and `sized_by_or_null` 
variants are equivalent to their base variants, except the pointer can be null 
regardless of count/size value.  If the pointer is null the size is effectively 
0.

rdar://125400354

>From fa82a978536906d330fb0fa6c67f2589f76df5e1 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_ols...@apple.com>
Date: Thu, 23 May 2024 11:44:41 -0700
Subject: [PATCH] [Bounds-Safety] Add sized_by, counted_by_or_null &
 sized_by_or_null

The attributes `sized_by`, `counted_by_or_null` and `sized_by_or_null`
have been added as variants on `counted_by`, each with slightly
different semantics.  `sized_by` takes a byte size parameter instead of
an element count, allowing pointees with unknown size. The
`counted_by_or_null` and `sized_by_or_null` variants are equivalent to
their base variants, except the pointer can be null regardless of
count/size value.  If the pointer is null the size is effectively 0.

rdar://125400354
---
 clang/docs/ReleaseNotes.rst                   |   6 +
 clang/include/clang/Basic/Attr.td             |  30 +++
 .../clang/Basic/DiagnosticSemaKinds.td        |  16 +-
 clang/include/clang/Sema/Sema.h               |   4 +-
 clang/lib/AST/TypePrinter.cpp                 |   3 +
 clang/lib/Parse/ParseDecl.cpp                 |  10 +-
 clang/lib/Sema/SemaDeclAttr.cpp               |  70 +++--
 clang/lib/Sema/SemaType.cpp                   |   8 +-
 clang/lib/Sema/TreeTransform.h                |   3 +-
 ...unted-by-or-null-late-parsed-struct-ptrs.c |  45 ++++
 .../AST/attr-counted-by-or-null-struct-ptrs.c | 117 ++++++++
 .../attr-sized-by-late-parsed-struct-ptrs.c   |  45 ++++
 ...sized-by-or-null-late-parsed-struct-ptrs.c |  45 ++++
 .../AST/attr-sized-by-or-null-struct-ptrs.c   | 117 ++++++++
 clang/test/AST/attr-sized-by-struct-ptrs.c    | 117 ++++++++
 .../attr-counted-by-or-null-late-parsed-off.c |  26 ++
 ...unted-by-or-null-late-parsed-struct-ptrs.c | 255 ++++++++++++++++++
 ...ed-by-or-null-struct-ptrs-sizeless-types.c |  17 ++
 .../attr-counted-by-or-null-struct-ptrs.c     | 225 ++++++++++++++++
 ...tr-counted-by-or-null-vla-sizeless-types.c |  11 +
 .../test/Sema/attr-sized-by-late-parsed-off.c |  26 ++
 .../attr-sized-by-late-parsed-struct-ptrs.c   | 243 +++++++++++++++++
 .../attr-sized-by-or-null-late-parsed-off.c   |  26 ++
 ...sized-by-or-null-late-parsed-struct-ptrs.c | 243 +++++++++++++++++
 ...ed-by-or-null-struct-ptrs-sizeless-types.c |  16 ++
 .../Sema/attr-sized-by-or-null-struct-ptrs.c  | 211 +++++++++++++++
 ...attr-sized-by-or-null-vla-sizeless-types.c |  11 +
 ...attr-sized-by-struct-ptrs-sizeless-types.c |  16 ++
 clang/test/Sema/attr-sized-by-struct-ptrs.c   | 211 +++++++++++++++
 .../Sema/attr-sized-by-vla-sizeless-types.c   |  11 +
 30 files changed, 2153 insertions(+), 31 deletions(-)
 create mode 100644 
clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c
 create mode 100644 clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
 create mode 100644 clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c
 create mode 100644 
clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c
 create mode 100644 clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
 create mode 100644 clang/test/AST/attr-sized-by-struct-ptrs.c
 create mode 100644 clang/test/Sema/attr-counted-by-or-null-late-parsed-off.c
 create mode 100644 
clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
 create mode 100644 
clang/test/Sema/attr-counted-by-or-null-struct-ptrs-sizeless-types.c
 create mode 100644 clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
 create mode 100644 clang/test/Sema/attr-counted-by-or-null-vla-sizeless-types.c
 create mode 100644 clang/test/Sema/attr-sized-by-late-parsed-off.c
 create mode 100644 clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
 create mode 100644 clang/test/Sema/attr-sized-by-or-null-late-parsed-off.c
 create mode 100644 
clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
 create mode 100644 
clang/test/Sema/attr-sized-by-or-null-struct-ptrs-sizeless-types.c
 create mode 100644 clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
 create mode 100644 clang/test/Sema/attr-sized-by-or-null-vla-sizeless-types.c
 create mode 100644 clang/test/Sema/attr-sized-by-struct-ptrs-sizeless-types.c
 create mode 100644 clang/test/Sema/attr-sized-by-struct-ptrs.c
 create mode 100644 clang/test/Sema/attr-sized-by-vla-sizeless-types.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2f83f5c6d54e9..841ef07f2e99b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -425,6 +425,12 @@ Attribute Changes in Clang
        size_t count;
      };
 
+- The attributes ``sized_by``, ``counted_by_or_null`` and ``sized_by_or_null```
+  have been added as variants on ``counted_by``, each with slightly different 
semantics.
+  ``sized_by`` takes a byte size parameter instead of an element count, 
allowing pointees
+  with unknown size. The ``counted_by_or_null`` and ``sized_by_or_null`` 
variants are equivalent
+  to their base variants, except the pointer can be null regardless of 
count/size value.
+  If the pointer is null the size is effectively 0.
 
 Improvements to Clang's diagnostics
 -----------------------------------
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 7a7721239a28f..70877f0dd2ba8 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2236,6 +2236,36 @@ def CountedBy : DeclOrTypeAttr {
   let LangOpts = [COnly];
 }
 
+def CountedByOrNull : DeclOrTypeAttr {
+  let Spellings = [Clang<"counted_by_or_null">];
+  let Subjects = SubjectList<[Field], ErrorDiag>;
+  let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>];
+  let LateParsed = LateAttrParseExperimentalExt;
+  let ParseArgumentsAsUnevaluated = 1;
+  let Documentation = [CountedByDocs];
+  let LangOpts = [COnly];
+}
+
+def SizedBy : DeclOrTypeAttr {
+  let Spellings = [Clang<"sized_by">];
+  let Subjects = SubjectList<[Field], ErrorDiag>;
+  let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>];
+  let LateParsed = LateAttrParseExperimentalExt;
+  let ParseArgumentsAsUnevaluated = 1;
+  let Documentation = [CountedByDocs];
+  let LangOpts = [COnly];
+}
+
+def SizedByOrNull : DeclOrTypeAttr {
+  let Spellings = [Clang<"sized_by_or_null">];
+  let Subjects = SubjectList<[Field], ErrorDiag>;
+  let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>];
+  let LateParsed = LateAttrParseExperimentalExt;
+  let ParseArgumentsAsUnevaluated = 1;
+  let Documentation = [CountedByDocs];
+  let LangOpts = [COnly];
+}
+
 // This is a marker used to indicate that an __unsafe_unretained qualifier was
 // ignored because ARC is not enabled. The usual representation for this
 // qualifier is as an ObjCOwnership attribute with Kind == "none".
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8e6596410c5d0..93672ccfd59d9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6532,27 +6532,27 @@ def warn_superclass_variable_sized_type_not_at_end : 
Warning<
   " in superclass %3">, InGroup<ObjCFlexibleArray>;
 
 def err_flexible_array_count_not_in_same_struct : Error<
-  "'counted_by' field %0 isn't within the same struct as the flexible array">;
+  "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' field 
%0 isn't within the same struct as the flexible array">;
 def err_counted_by_attr_not_on_ptr_or_flexible_array_member : Error<
-  "'counted_by' only applies to pointers or C99 flexible array members">;
+  "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' only 
applies to pointers%select{ or C99 flexible array members|||}0">;
 def err_counted_by_attr_on_array_not_flexible_array_member : Error<
   "'counted_by' on arrays only applies to C99 flexible array members">;
 def err_counted_by_attr_refer_to_itself : Error<
   "'counted_by' cannot refer to the flexible array member %0">;
 def err_counted_by_must_be_in_structure : Error<
-  "field %0 in 'counted_by' not inside structure">;
+  "field %0 in 
'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' not inside 
structure">;
 def err_counted_by_attr_argument_not_integer : Error<
-  "'counted_by' requires a non-boolean integer type argument">;
+  "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' 
requires a non-boolean integer type argument">;
 def err_counted_by_attr_only_support_simple_decl_reference : Error<
-  "'counted_by' argument must be a simple declaration reference">;
+  "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' 
argument must be a simple declaration reference">;
 def err_counted_by_attr_in_union : Error<
-  "'counted_by' cannot be applied to a union member">;
+  "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' cannot 
be applied to a union member">;
 def err_counted_by_attr_refer_to_union : Error<
-  "'counted_by' argument cannot refer to a union member">;
+  "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' 
argument cannot refer to a union member">;
 def note_flexible_array_counted_by_attr_field : Note<
   "field %0 declared here">;
 def err_counted_by_attr_pointee_unknown_size : Error<
-  "'counted_by' cannot be applied to %select{"
+  "'%select{counted_by|counted_by_or_null}3' cannot be applied to %select{"
     "a pointer with pointee|" // pointer
     "an array with element}0" // array
   " of unknown size because %1 is %select{"
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index cd06bd9c230df..2ea7eb71a8f2e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11397,7 +11397,9 @@ class Sema final : public SemaBase {
                            SourceLocation AttrLoc);
 
   QualType BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
-                                                  Expr *CountExpr);
+                                                  Expr *CountExpr,
+                                                  bool CountInBytes,
+                                                  bool OrNull);
 
   QualType BuildAddressSpaceAttr(QualType &T, LangAS ASIdx, Expr *AddrSpace,
                                  SourceLocation AttrLoc);
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 87f0a8728d850..6f346ca894f60 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1905,6 +1905,9 @@ void TypePrinter::printAttributedAfter(const 
AttributedType *T,
     break;
 
   case attr::CountedBy:
+  case attr::CountedByOrNull:
+  case attr::SizedBy:
+  case attr::SizedByOrNull:
   case attr::LifetimeBound:
   case attr::TypeNonNull:
   case attr::TypeNullable:
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 8405b44685ae4..1dc307484204c 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -662,7 +662,10 @@ void Parser::ParseGNUAttributeArgs(
     ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, ScopeName,
                               ScopeLoc, Form);
     return;
-  } else if (AttrKind == ParsedAttr::AT_CountedBy) {
+  } else if (AttrKind == ParsedAttr::AT_CountedBy ||
+             AttrKind == ParsedAttr::AT_CountedByOrNull ||
+             AttrKind == ParsedAttr::AT_SizedBy ||
+             AttrKind == ParsedAttr::AT_SizedByOrNull) {
     ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc,
                          Form);
     return;
@@ -4806,7 +4809,7 @@ static void 
DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS,
       if (!RD->containsDecl(DD.getDecl())) {
         P.Diag(VD->getBeginLoc(),
                diag::err_flexible_array_count_not_in_same_struct)
-            << DD.getDecl();
+            << DD.getDecl() << CAT->getKind();
         P.Diag(DD.getDecl()->getBeginLoc(),
                diag::note_flexible_array_counted_by_attr_field)
             << DD.getDecl();
@@ -4964,6 +4967,9 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA,
                                 /*SyntaxUsed=*/ParsedForm.getSyntax());
   switch (AttrKind) {
   case ParsedAttr::Kind::AT_CountedBy:
+  case ParsedAttr::Kind::AT_CountedByOrNull:
+  case ParsedAttr::Kind::AT_SizedBy:
+  case ParsedAttr::Kind::AT_SizedByOrNull:
     ParseBoundsAttribute(LA.AttrName, LA.AttrNameLoc, Attrs,
                          /*ScopeName=*/ScopeName, SourceLocation(),
                          /*Form=*/ParsedForm);
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index c8b71631076ba..ebce77d368e50 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -8641,22 +8641,33 @@ enum class CountedByInvalidPointeeTypeKind {
   VALID,
 };
 
-static bool CheckCountedByAttrOnField(
-    Sema &S, FieldDecl *FD, Expr *E,
-    llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) {
+static bool
+CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E,
+                          llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls,
+                          bool CountInBytes, bool OrNull) {
   // Check the context the attribute is used in
 
+  unsigned Kind = CountInBytes;
+  if (OrNull)
+    Kind += 2;
+
   if (FD->getParent()->isUnion()) {
     S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union)
-        << FD->getSourceRange();
+        << Kind << FD->getSourceRange();
     return true;
   }
 
   const auto FieldTy = FD->getType();
+  if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
+    S.Diag(FD->getBeginLoc(),
+           diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
+        << Kind << FD->getLocation();
+    return true;
+  }
   if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
     S.Diag(FD->getBeginLoc(),
            diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
-        << FD->getLocation();
+        << Kind << FD->getLocation();
     return true;
   }
 
@@ -8667,7 +8678,7 @@ static bool CheckCountedByAttrOnField(
                                        StrictFlexArraysLevel, true)) {
     S.Diag(FD->getBeginLoc(),
            diag::err_counted_by_attr_on_array_not_flexible_array_member)
-        << FD->getLocation();
+        << Kind << FD->getLocation();
     return true;
   }
 
@@ -8697,9 +8708,10 @@ static bool CheckCountedByAttrOnField(
     InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
   }
 
-  if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
+  if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID &&
+      !CountInBytes) {
     S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_pointee_unknown_size)
-        << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
+        << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind << OrNull
         << FD->getSourceRange();
     return true;
   }
@@ -8708,7 +8720,7 @@ static bool CheckCountedByAttrOnField(
 
   if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
     S.Diag(E->getBeginLoc(), diag::err_counted_by_attr_argument_not_integer)
-        << E->getSourceRange();
+        << Kind << E->getSourceRange();
     return true;
   }
 
@@ -8716,7 +8728,7 @@ static bool CheckCountedByAttrOnField(
   if (!DRE) {
     S.Diag(E->getBeginLoc(),
            diag::err_counted_by_attr_only_support_simple_decl_reference)
-        << E->getSourceRange();
+        << Kind << E->getSourceRange();
     return true;
   }
 
@@ -8727,7 +8739,7 @@ static bool CheckCountedByAttrOnField(
   }
   if (!CountFD) {
     S.Diag(E->getBeginLoc(), diag::err_counted_by_must_be_in_structure)
-        << CountDecl << E->getSourceRange();
+        << CountDecl << Kind << E->getSourceRange();
 
     S.Diag(CountDecl->getBeginLoc(),
            diag::note_flexible_array_counted_by_attr_field)
@@ -8738,7 +8750,7 @@ static bool CheckCountedByAttrOnField(
   if (FD->getParent() != CountFD->getParent()) {
     if (CountFD->getParent()->isUnion()) {
       S.Diag(CountFD->getBeginLoc(), diag::err_counted_by_attr_refer_to_union)
-          << CountFD->getSourceRange();
+          << Kind << CountFD->getSourceRange();
       return true;
     }
     // Whether CountRD is an anonymous struct is not determined at this
@@ -8750,7 +8762,7 @@ static bool CheckCountedByAttrOnField(
     if (RD != CountRD) {
       S.Diag(E->getBeginLoc(),
              diag::err_flexible_array_count_not_in_same_struct)
-          << CountFD << E->getSourceRange();
+          << CountFD << Kind << E->getSourceRange();
       S.Diag(CountFD->getBeginLoc(),
              diag::note_flexible_array_counted_by_attr_field)
           << CountFD << CountFD->getSourceRange();
@@ -8770,12 +8782,35 @@ static void handleCountedByAttrField(Sema &S, Decl *D, 
const ParsedAttr &AL) {
   if (!CountExpr)
     return;
 
+  bool CountInBytes;
+  bool OrNull;
+  switch (AL.getKind()) {
+  case ParsedAttr::AT_CountedBy:
+    CountInBytes = false;
+    OrNull = false;
+    break;
+  case ParsedAttr::AT_CountedByOrNull:
+    CountInBytes = false;
+    OrNull = true;
+    break;
+  case ParsedAttr::AT_SizedBy:
+    CountInBytes = true;
+    OrNull = false;
+    break;
+  case ParsedAttr::AT_SizedByOrNull:
+    CountInBytes = true;
+    OrNull = true;
+    break;
+  default:
+    llvm_unreachable("unexpected counted_by family attribute");
+  }
+
   llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
-  if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls))
+  if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls, CountInBytes, OrNull))
     return;
 
-  QualType CAT =
-      S.BuildCountAttributedArrayOrPointerType(FD->getType(), CountExpr);
+  QualType CAT = S.BuildCountAttributedArrayOrPointerType(
+      FD->getType(), CountExpr, CountInBytes, OrNull);
   FD->setType(CAT);
 }
 
@@ -9802,6 +9837,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 
const ParsedAttr &AL,
     break;
 
   case ParsedAttr::AT_CountedBy:
+  case ParsedAttr::AT_CountedByOrNull:
+  case ParsedAttr::AT_SizedBy:
+  case ParsedAttr::AT_SizedByOrNull:
     handleCountedByAttrField(S, D, AL);
     break;
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index ef0b6b701a52c..832a30992f24a 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -9346,15 +9346,17 @@ BuildTypeCoupledDecls(Expr *E,
 }
 
 QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
-                                                      Expr *CountExpr) {
+                                                      Expr *CountExpr,
+                                                      bool CountInBytes,
+                                                      bool OrNull) {
   assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType());
 
   llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
   BuildTypeCoupledDecls(CountExpr, Decls);
   /// When the resulting expression is invalid, we still create the AST using
   /// the original count expression for the sake of AST dump.
-  return Context.getCountAttributedType(
-      WrappedTy, CountExpr, /*CountInBytes*/ false, /*OrNull*/ false, Decls);
+  return Context.getCountAttributedType(WrappedTy, CountExpr, CountInBytes,
+                                        OrNull, Decls);
 }
 
 /// getDecltypeForExpr - Given an expr, will return the decltype for
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 29444f0edc2ae..cf96c9e2af68f 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7344,7 +7344,8 @@ QualType 
TreeTransform<Derived>::TransformCountAttributedType(
   if (getDerived().AlwaysRebuild() || InnerTy != OldTy->desugar() ||
       OldCount != NewCount) {
     // Currently, CountAttributedType can only wrap incomplete array types.
-    Result = SemaRef.BuildCountAttributedArrayOrPointerType(InnerTy, NewCount);
+    Result = SemaRef.BuildCountAttributedArrayOrPointerType(
+        InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull());
   }
 
   TLB.push<CountAttributedTypeLoc>(Result);
diff --git a/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c 
b/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c
new file mode 100644
index 0000000000000..975c0a0231943
--- /dev/null
+++ b/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes %s -ast-dump | 
FileCheck %s
+
+#define __counted_by_or_null(f)  __attribute__((counted_by_or_null(f)))
+
+struct size_known {
+  int field;
+};
+
+//==============================================================================
+// __counted_by_or_null on struct member pointer in decl attribute position
+//==============================================================================
+
+struct on_member_pointer_complete_ty {
+  struct size_known *buf __counted_by_or_null(count);
+  int count;
+};
+// CHECK-LABEL: struct on_member_pointer_complete_ty definition
+// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int'
+
+struct on_pointer_anon_count {
+  struct size_known *buf __counted_by_or_null(count);
+  struct {
+    int count;
+  };
+};
+
+// CHECK-LABEL: struct on_pointer_anon_count definition
+// CHECK-NEXT:  |-FieldDecl {{.*}} buf 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:  |-RecordDecl {{.*}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.*}} count 'int'
+// CHECK-NEXT:  |-FieldDecl {{.*}} implicit 'struct 
on_pointer_anon_count::(anonymous at {{.*}})'
+// CHECK-NEXT:  `-IndirectFieldDecl {{.*}} implicit referenced count 'int'
+// CHECK-NEXT:    |-Field {{.*}} '' 'struct on_pointer_anon_count::(anonymous 
at {{.*}})'
+// CHECK-NEXT:    `-Field {{.*}} 'count' 'int'
+
+//==============================================================================
+// __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.
+//
+// See `clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c` for test
+// cases.
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
new file mode 100644
index 0000000000000..cedb3f1192eda
--- /dev/null
+++ b/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
@@ -0,0 +1,117 @@
+// RUN: %clang_cc1 %s -ast-dump | FileCheck %s
+
+#define __counted_by_or_null(f)  __attribute__((counted_by_or_null(f)))
+
+struct size_unknown;
+struct size_known {
+  int field;
+};
+
+//==============================================================================
+// __counted_by_or_null on struct member pointer in decl attribute position
+//==============================================================================
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_complete_ty 
definition
+// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+struct on_member_pointer_complete_ty {
+  int count;
+  struct size_known * buf __counted_by_or_null(count);
+};
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_buf definition
+// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT:  |-RecordDecl {{.+}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.+}} buf 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:  |-FieldDecl {{.+}} implicit 'struct 
on_pointer_anon_buf::(anonymous at [[ANON_STRUCT_PATH:.+]])'
+// CHECK-NEXT:  `-IndirectFieldDecl {{.+}} implicit buf 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:    |-Field {{.+}} '' 'struct on_pointer_anon_buf::(anonymous at 
[[ANON_STRUCT_PATH]])'
+// CHECK-NEXT:    `-Field {{.+}} 'buf' 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+struct on_pointer_anon_buf {
+  int count;
+  struct {
+    struct size_known *buf __counted_by_or_null(count);
+  };
+};
+
+struct on_pointer_anon_count {
+  struct {
+    int count;
+  };
+  struct size_known *buf __counted_by_or_null(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
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_complete_ty_ty_pos 
definition
+// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+struct on_member_pointer_complete_ty_ty_pos {
+  int count;
+  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'
+// CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known ** 
__counted_by_or_null(count)':'struct size_known **'
+struct on_nested_pointer_outer {
+  int count;
+  struct size_known **__counted_by_or_null(count) buf;
+};
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_buf_ty_pos definition
+// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT:  |-RecordDecl {{.+}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.+}} buf 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:  |-FieldDecl {{.+}} implicit 'struct 
on_pointer_anon_buf_ty_pos::(anonymous at [[ANON_STRUCT_PATH2:.+]])'
+// CHECK-NEXT:  `-IndirectFieldDecl {{.+}} implicit buf 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:    |-Field {{.+}} '' 'struct 
on_pointer_anon_buf_ty_pos::(anonymous at [[ANON_STRUCT_PATH2]])'
+// CHECK-NEXT:    `-Field {{.+}} 'buf' 'struct size_known * 
__counted_by_or_null(count)':'struct size_known *'
+struct on_pointer_anon_buf_ty_pos {
+  int count;
+  struct {
+    struct size_known * __counted_by_or_null(count) buf;
+  };
+};
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_count_ty_pos 
definition
+// CHECK-NEXT:  |-RecordDecl {{.+}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.+}} count 'int'
+// CHECK-NEXT:  |-FieldDecl {{.+}} implicit 'struct 
on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])'
+// CHECK-NEXT:  |-IndirectFieldDecl {{.+}} implicit referenced count 'int'
+// CHECK-NEXT:  | |-Field {{.+}} '' 'struct 
on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3]])'
+// CHECK-NEXT:  | `-Field {{.+}} 'count' 'int'
+struct on_pointer_anon_count_ty_pos {
+  struct {
+    int count;
+  };
+  struct size_known *__counted_by_or_null(count) buf;
+};
diff --git a/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c 
b/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c
new file mode 100644
index 0000000000000..b58caf608bf97
--- /dev/null
+++ b/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes %s -ast-dump | 
FileCheck %s
+
+#define __sized_by(f)  __attribute__((sized_by(f)))
+
+struct size_known {
+  int field;
+};
+
+//==============================================================================
+// __sized_by on struct member pointer in decl attribute position
+//==============================================================================
+
+struct on_member_pointer_complete_ty {
+  struct size_known *buf __sized_by(count);
+  int count;
+};
+// CHECK-LABEL: struct on_member_pointer_complete_ty definition
+// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * 
__sized_by(count)':'struct size_known *'
+// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int'
+
+struct on_pointer_anon_count {
+  struct size_known *buf __sized_by(count);
+  struct {
+    int count;
+  };
+};
+
+// CHECK-LABEL: struct on_pointer_anon_count definition
+// CHECK-NEXT:  |-FieldDecl {{.*}} buf 'struct size_known * 
__sized_by(count)':'struct size_known *'
+// CHECK-NEXT:  |-RecordDecl {{.*}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.*}} count 'int'
+// CHECK-NEXT:  |-FieldDecl {{.*}} implicit 'struct 
on_pointer_anon_count::(anonymous at {{.*}})'
+// CHECK-NEXT:  `-IndirectFieldDecl {{.*}} implicit referenced count 'int'
+// CHECK-NEXT:    |-Field {{.*}} '' 'struct on_pointer_anon_count::(anonymous 
at {{.*}})'
+// CHECK-NEXT:    `-Field {{.*}} 'count' 'int'
+
+//==============================================================================
+// __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 
`count`
+// field being unavailable.
+//
+// See `clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c` for test
+// cases.
diff --git a/clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c 
b/clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c
new file mode 100644
index 0000000000000..d55a42ac0fb94
--- /dev/null
+++ b/clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes %s -ast-dump | 
FileCheck %s
+
+#define __sized_by_or_null(f)  __attribute__((sized_by_or_null(f)))
+
+struct size_known {
+  int field;
+};
+
+//==============================================================================
+// __sized_by_or_null on struct member pointer in decl attribute position
+//==============================================================================
+
+struct on_member_pointer_complete_ty {
+  struct size_known *buf __sized_by_or_null(count);
+  int count;
+};
+// CHECK-LABEL: struct on_member_pointer_complete_ty definition
+// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int'
+
+struct on_pointer_anon_count {
+  struct size_known *buf __sized_by_or_null(count);
+  struct {
+    int count;
+  };
+};
+
+// CHECK-LABEL: struct on_pointer_anon_count definition
+// CHECK-NEXT:  |-FieldDecl {{.*}} buf 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:  |-RecordDecl {{.*}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.*}} count 'int'
+// CHECK-NEXT:  |-FieldDecl {{.*}} implicit 'struct 
on_pointer_anon_count::(anonymous at {{.*}})'
+// CHECK-NEXT:  `-IndirectFieldDecl {{.*}} implicit referenced count 'int'
+// CHECK-NEXT:    |-Field {{.*}} '' 'struct on_pointer_anon_count::(anonymous 
at {{.*}})'
+// CHECK-NEXT:    `-Field {{.*}} 'count' 'int'
+
+//==============================================================================
+// __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 
`count`
+// field being unavailable.
+//
+// See `clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c` for test
+// cases.
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
new file mode 100644
index 0000000000000..6189799b85ccb
--- /dev/null
+++ b/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
@@ -0,0 +1,117 @@
+// RUN: %clang_cc1 %s -ast-dump | FileCheck %s
+
+#define __sized_by_or_null(f)  __attribute__((sized_by_or_null(f)))
+
+struct size_unknown;
+struct size_known {
+  int field;
+};
+
+//==============================================================================
+// __sized_by_or_null on struct member pointer in decl attribute position
+//==============================================================================
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_complete_ty 
definition
+// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+struct on_member_pointer_complete_ty {
+  int count;
+  struct size_known * buf __sized_by_or_null(count);
+};
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_buf definition
+// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT:  |-RecordDecl {{.+}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.+}} buf 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:  |-FieldDecl {{.+}} implicit 'struct 
on_pointer_anon_buf::(anonymous at [[ANON_STRUCT_PATH:.+]])'
+// CHECK-NEXT:  `-IndirectFieldDecl {{.+}} implicit buf 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:    |-Field {{.+}} '' 'struct on_pointer_anon_buf::(anonymous at 
[[ANON_STRUCT_PATH]])'
+// CHECK-NEXT:    `-Field {{.+}} 'buf' 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+struct on_pointer_anon_buf {
+  int count;
+  struct {
+    struct size_known *buf __sized_by_or_null(count);
+  };
+};
+
+struct on_pointer_anon_count {
+  struct {
+    int count;
+  };
+  struct size_known *buf __sized_by_or_null(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
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_complete_ty_ty_pos 
definition
+// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+struct on_member_pointer_complete_ty_ty_pos {
+  int count;
+  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 **'
+struct on_nested_pointer_outer {
+  int count;
+  struct size_known **__sized_by_or_null(count) buf;
+};
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_buf_ty_pos definition
+// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT:  |-RecordDecl {{.+}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.+}} buf 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:  |-FieldDecl {{.+}} implicit 'struct 
on_pointer_anon_buf_ty_pos::(anonymous at [[ANON_STRUCT_PATH2:.+]])'
+// CHECK-NEXT:  `-IndirectFieldDecl {{.+}} implicit buf 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+// CHECK-NEXT:    |-Field {{.+}} '' 'struct 
on_pointer_anon_buf_ty_pos::(anonymous at [[ANON_STRUCT_PATH2]])'
+// CHECK-NEXT:    `-Field {{.+}} 'buf' 'struct size_known * 
__sized_by_or_null(count)':'struct size_known *'
+struct on_pointer_anon_buf_ty_pos {
+  int count;
+  struct {
+    struct size_known * __sized_by_or_null(count) buf;
+  };
+};
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_count_ty_pos 
definition
+// CHECK-NEXT:  |-RecordDecl {{.+}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.+}} count 'int'
+// CHECK-NEXT:  |-FieldDecl {{.+}} implicit 'struct 
on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])'
+// CHECK-NEXT:  |-IndirectFieldDecl {{.+}} implicit referenced count 'int'
+// CHECK-NEXT:  | |-Field {{.+}} '' 'struct 
on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3]])'
+// CHECK-NEXT:  | `-Field {{.+}} 'count' 'int'
+struct on_pointer_anon_count_ty_pos {
+  struct {
+    int count;
+  };
+  struct size_known *__sized_by_or_null(count) buf;
+};
diff --git a/clang/test/AST/attr-sized-by-struct-ptrs.c 
b/clang/test/AST/attr-sized-by-struct-ptrs.c
new file mode 100644
index 0000000000000..5d9ed0094c685
--- /dev/null
+++ b/clang/test/AST/attr-sized-by-struct-ptrs.c
@@ -0,0 +1,117 @@
+// RUN: %clang_cc1 %s -ast-dump | FileCheck %s
+
+#define __sized_by(f)  __attribute__((sized_by(f)))
+
+struct size_unknown;
+struct size_known {
+  int field;
+};
+
+//==============================================================================
+// __sized_by on struct member pointer in decl attribute position
+//==============================================================================
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_complete_ty 
definition
+// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known * 
__sized_by(count)':'struct size_known *'
+struct on_member_pointer_complete_ty {
+  int count;
+  struct size_known * buf __sized_by(count);
+};
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_buf definition
+// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT:  |-RecordDecl {{.+}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.+}} buf 'struct size_known * 
__sized_by(count)':'struct size_known *'
+// CHECK-NEXT:  |-FieldDecl {{.+}} implicit 'struct 
on_pointer_anon_buf::(anonymous at [[ANON_STRUCT_PATH:.+]])'
+// CHECK-NEXT:  `-IndirectFieldDecl {{.+}} implicit buf 'struct size_known * 
__sized_by(count)':'struct size_known *'
+// CHECK-NEXT:    |-Field {{.+}} '' 'struct on_pointer_anon_buf::(anonymous at 
[[ANON_STRUCT_PATH]])'
+// CHECK-NEXT:    `-Field {{.+}} 'buf' 'struct size_known * 
__sized_by(count)':'struct size_known *'
+struct on_pointer_anon_buf {
+  int count;
+  struct {
+    struct size_known *buf __sized_by(count);
+  };
+};
+
+struct on_pointer_anon_count {
+  struct {
+    int count;
+  };
+  struct size_known *buf __sized_by(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
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_complete_ty_ty_pos 
definition
+// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known * 
__sized_by(count)':'struct size_known *'
+struct on_member_pointer_complete_ty_ty_pos {
+  int count;
+  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 **'
+struct on_nested_pointer_outer {
+  int count;
+  struct size_known **__sized_by(count) buf;
+};
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_buf_ty_pos definition
+// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
+// CHECK-NEXT:  |-RecordDecl {{.+}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.+}} buf 'struct size_known * 
__sized_by(count)':'struct size_known *'
+// CHECK-NEXT:  |-FieldDecl {{.+}} implicit 'struct 
on_pointer_anon_buf_ty_pos::(anonymous at [[ANON_STRUCT_PATH2:.+]])'
+// CHECK-NEXT:  `-IndirectFieldDecl {{.+}} implicit buf 'struct size_known * 
__sized_by(count)':'struct size_known *'
+// CHECK-NEXT:    |-Field {{.+}} '' 'struct 
on_pointer_anon_buf_ty_pos::(anonymous at [[ANON_STRUCT_PATH2]])'
+// CHECK-NEXT:    `-Field {{.+}} 'buf' 'struct size_known * 
__sized_by(count)':'struct size_known *'
+struct on_pointer_anon_buf_ty_pos {
+  int count;
+  struct {
+    struct size_known * __sized_by(count) buf;
+  };
+};
+
+// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_count_ty_pos 
definition
+// CHECK-NEXT:  |-RecordDecl {{.+}} struct definition
+// CHECK-NEXT:  | `-FieldDecl {{.+}} count 'int'
+// CHECK-NEXT:  |-FieldDecl {{.+}} implicit 'struct 
on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])'
+// CHECK-NEXT:  |-IndirectFieldDecl {{.+}} implicit referenced count 'int'
+// CHECK-NEXT:  | |-Field {{.+}} '' 'struct 
on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3]])'
+// CHECK-NEXT:  | `-Field {{.+}} 'count' 'int'
+struct on_pointer_anon_count_ty_pos {
+  struct {
+    int count;
+  };
+  struct size_known *__sized_by(count) buf;
+};
diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-off.c 
b/clang/test/Sema/attr-counted-by-or-null-late-parsed-off.c
new file mode 100644
index 0000000000000..0e76ad9e48b4e
--- /dev/null
+++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-off.c
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -DNEEDS_LATE_PARSING 
-fno-experimental-late-parse-attributes -fsyntax-only -verify %s
+// RUN: %clang_cc1 -DNEEDS_LATE_PARSING -fsyntax-only -verify %s
+
+// RUN: %clang_cc1 -UNEEDS_LATE_PARSING 
-fno-experimental-late-parse-attributes -fsyntax-only -verify=ok %s
+// RUN: %clang_cc1 -UNEEDS_LATE_PARSING -fsyntax-only -verify=ok %s
+
+#define __counted_by_or_null(f)  __attribute__((counted_by_or_null(f)))
+
+struct size_known { int dummy; };
+
+#ifdef NEEDS_LATE_PARSING
+struct on_decl {
+  // expected-error@+1{{use of undeclared identifier 'count'}}
+  struct size_known *buf __counted_by_or_null(count);
+  int count;
+};
+
+#else
+
+// ok-no-diagnostics
+struct on_decl {
+  int count;
+  struct size_known *buf __counted_by_or_null(count);
+};
+
+#endif
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
new file mode 100644
index 0000000000000..4c5c0ebfb52c3
--- /dev/null
+++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
@@ -0,0 +1,255 @@
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify 
%s
+
+#define __counted_by_or_null(f)  __attribute__((counted_by_or_null(f)))
+#define __counted_by(f)  __attribute__((counted_by(f)))
+
+struct size_unknown;
+struct size_known {
+  int field;
+};
+
+typedef void(*fn_ptr_ty)(void);
+
+//==============================================================================
+// __counted_by_or_null on struct member pointer in decl attribute position
+//==============================================================================
+
+struct on_member_pointer_complete_ty {
+  struct size_known * buf __counted_by_or_null(count);
+  int count;
+};
+
+struct on_member_pointer_incomplete_ty {
+  struct size_unknown * buf __counted_by_or_null(count); // 
expected-error{{'counted_by_or_null' cannot be applied to a pointer with 
pointee of unknown size because 'struct size_unknown' is an incomplete type}}
+  int count;
+};
+
+struct on_member_pointer_const_incomplete_ty {
+  // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because 'const struct size_unknown' is an 
incomplete type}}
+  const struct size_unknown * buf __counted_by_or_null(count);
+  int count;
+};
+
+struct on_member_pointer_void_ty {
+  void* buf __counted_by_or_null(count); // 
expected-error{{'counted_by_or_null' cannot be applied to a pointer with 
pointee of unknown size because 'void' is an incomplete type}}
+  int count;
+};
+
+struct on_member_pointer_fn_ptr_ty {
+  // buffer of `count` function pointers is allowed
+  void (**fn_ptr)(void) __counted_by_or_null(count);
+  int count;
+};
+
+
+struct on_member_pointer_fn_ptr_ty_ptr_ty {
+  // buffer of `count` function pointers is allowed
+  fn_ptr_ty* fn_ptr __counted_by_or_null(count);
+  int count;
+};
+
+struct on_member_pointer_fn_ty {
+  // buffer of `count` functions is not allowed
+  // 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 (*fn_ptr)(void) __counted_by_or_null(count);
+  int count;
+};
+
+struct on_member_pointer_fn_ptr_ty_ty {
+  // buffer of `count` functions is not allowed
+  // 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 fn_ptr __counted_by_or_null(count);
+  int count;
+};
+
+struct has_unannotated_vla {
+  int count;
+  int buffer[];
+};
+
+struct on_member_pointer_struct_with_vla {
+  // expected-error@+1{{'counted_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* objects __counted_by_or_null(count);
+  int count;
+};
+
+struct has_annotated_vla {
+  int count;
+  int buffer[] __counted_by(count);
+};
+
+// Currently prevented because computing the size of `objects` at runtime would
+// require an O(N) walk of `objects` to take into account the length of the VLA
+// in each struct instance.
+struct on_member_pointer_struct_with_annotated_vla {
+  // 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* objects __counted_by_or_null(count);
+  int count;
+};
+
+struct on_pointer_anon_buf {
+  // TODO: Support referring to parent scope
+  struct {
+    // expected-error@+1{{use of undeclared identifier 'count'}}
+    struct size_known *buf __counted_by_or_null(count);
+  };
+  int count;
+};
+
+struct on_pointer_anon_count {
+  struct size_known *buf __counted_by_or_null(count);
+  struct {
+    int 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'}}
+  void *__counted_by_or_null(count) buf;
+  int count;
+};
+
+// -
+
+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'}}
+  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'}}
+  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'}}
+  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'}}
+  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'}}
+  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'}}
+  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;
+};
+
+struct on_pointer_anon_buf_ty_pos {
+  struct {
+    // TODO: Support referring to parent scope
+    // expected-error@+1{{use of undeclared identifier 'count'}}
+    struct size_known * __counted_by_or_null(count) buf;
+  };
+  int count;
+};
+
+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;
+  };
+};
+
+//==============================================================================
+// __counted_by_or_null on struct non-pointer members
+//==============================================================================
+
+struct on_pod_ty {
+  // expected-error@+1{{'counted_by_or_null' only applies to pointers}}
+  int wrong_ty __counted_by_or_null(count);
+  int count;
+};
+
+struct on_void_ty {
+  // expected-error@+2{{'counted_by_or_null' only applies to pointers}}
+  // expected-error@+1{{field has incomplete type 'void'}}
+  void wrong_ty __counted_by_or_null(count);
+  int count;
+};
diff --git 
a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-sizeless-types.c 
b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-sizeless-types.c
new file mode 100644
index 0000000000000..3c84b90fac3ad
--- /dev/null
+++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-sizeless-types.c
@@ -0,0 +1,17 @@
+// __SVInt8_t is specific to ARM64 so specify that in the target triple
+// RUN: %clang_cc1 -triple arm64-apple-darwin -fsyntax-only -verify %s
+
+#define __counted_by_or_null(f)  __attribute__((counted_by_or_null(f)))
+
+struct on_sizeless_pointee_ty {
+    int count;
+    // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because '__SVInt8_t' is a sizeless type}}
+    __SVInt8_t* member __counted_by_or_null(count);
+};
+
+struct on_sizeless_ty {
+    int count;
+    // expected-error@+2{{'counted_by_or_null' only applies to pointers}}
+    // expected-error@+1{{field has sizeless type '__SVInt8_t'}}
+    __SVInt8_t member __counted_by_or_null(count);
+};
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
new file mode 100644
index 0000000000000..a38b363d1b5b2
--- /dev/null
+++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
@@ -0,0 +1,225 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#define __counted_by_or_null(f)  __attribute__((counted_by_or_null(f)))
+#define __counted_by(f)  __attribute__((counted_by(f)))
+
+struct size_unknown;
+struct size_known {
+  int field;
+};
+
+typedef void(*fn_ptr_ty)(void);
+
+//==============================================================================
+// __counted_by_or_null on struct member pointer in decl attribute position
+//==============================================================================
+
+struct on_member_pointer_complete_ty {
+  int count;
+  struct size_known * buf __counted_by_or_null(count);
+};
+
+struct on_member_pointer_incomplete_ty {
+  int count;
+  // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because 'struct size_unknown' is an incomplete 
type}}
+  struct size_unknown * buf __counted_by_or_null(count);
+};
+
+struct on_member_pointer_const_incomplete_ty {
+  int count;
+  // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because 'const struct size_unknown' is an 
incomplete type}}
+  const struct size_unknown * buf __counted_by_or_null(count);
+};
+
+struct on_member_pointer_void_ty {
+  int 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* buf __counted_by_or_null(count);
+};
+
+struct on_member_pointer_fn_ptr_ty {
+  int count;
+  // buffer of `count` function pointers is allowed
+  void (**fn_ptr)(void) __counted_by_or_null(count);
+};
+
+struct on_member_pointer_fn_ptr_ty_ptr_ty {
+  int count;
+  // buffer of `count` function pointers is allowed
+  fn_ptr_ty* fn_ptr __counted_by_or_null(count);
+};
+
+struct on_member_pointer_fn_ty {
+  int count;
+  // buffer of `count` functions is not allowed
+  // 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 (*fn_ptr)(void) __counted_by_or_null(count);
+};
+
+struct on_member_pointer_fn_ptr_ty_ty {
+  int count;
+  // buffer of `count` functions is not allowed
+  // 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 fn_ptr __counted_by_or_null(count);
+};
+
+struct has_unannotated_vla {
+  int count;
+  int buffer[];
+};
+
+struct on_member_pointer_struct_with_vla {
+  int count;
+  // expected-error@+1{{'counted_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* objects __counted_by_or_null(count);
+};
+
+struct has_annotated_vla {
+  int count;
+  int buffer[] __counted_by(count);
+};
+
+// Currently prevented because computing the size of `objects` at runtime would
+// require an O(N) walk of `objects` to take into account the length of the VLA
+// in each struct instance.
+struct on_member_pointer_struct_with_annotated_vla {
+  int 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* objects __counted_by_or_null(count);
+};
+
+struct on_pointer_anon_buf {
+  int count;
+  struct {
+    struct size_known *buf __counted_by_or_null(count);
+  };
+};
+
+struct on_pointer_anon_count {
+  struct {
+    int count;
+  };
+  struct size_known *buf __counted_by_or_null(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;
+  struct size_known *__counted_by_or_null(count) buf;
+};
+
+struct on_member_pointer_incomplete_ty_ty_pos {
+  int count;
+  // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because 'struct size_unknown' is an incomplete 
type}}
+  struct size_unknown * __counted_by_or_null(count) buf;
+};
+
+struct on_member_pointer_const_incomplete_ty_ty_pos {
+  int count;
+  // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because 'const struct size_unknown' is an 
incomplete type}}
+  const struct size_unknown * __counted_by_or_null(count) buf;
+};
+
+struct on_member_pointer_void_ty_ty_pos {
+  int 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;
+};
+
+// -
+
+struct on_member_pointer_fn_ptr_ty_pos {
+  int count;
+  // buffer of `count` function pointers is allowed
+  void (** __counted_by_or_null(count) fn_ptr)(void);
+};
+
+struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
+  int count;
+  // buffer of `count` function pointers is allowed
+  fn_ptr_ty* __counted_by_or_null(count) fn_ptr;
+};
+
+struct on_member_pointer_fn_ty_ty_pos {
+  int count;
+  // buffer of `count` functions is not allowed
+  // 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);
+};
+
+struct on_member_pointer_fn_ptr_ty_ty_pos {
+  int count;
+  // buffer of `count` functions is not allowed
+  // 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;
+};
+
+// 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;
+  void (* __counted_by_or_null(count) * fn_ptr)(void);
+};
+
+struct on_member_pointer_struct_with_vla_ty_pos {
+  int count;
+  // expected-error@+1{{'counted_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 *__counted_by_or_null(count) objects;
+};
+
+// Currently prevented because computing the size of `objects` at runtime would
+// require an O(N) walk of `objects` to take into account the length of the VLA
+// in each struct instance.
+struct on_member_pointer_struct_with_annotated_vla_ty_pos {
+  int 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;
+};
+
+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;
+  struct size_known *__counted_by_or_null(count) *buf;
+};
+
+struct on_nested_pointer_outer {
+  int count;
+  struct size_known **__counted_by_or_null(count) buf;
+};
+
+struct on_pointer_anon_buf_ty_pos {
+  int count;
+  struct {
+    struct size_known * __counted_by_or_null(count) buf;
+  };
+};
+
+struct on_pointer_anon_count_ty_pos {
+  struct {
+    int count;
+  };
+  struct size_known *__counted_by_or_null(count) buf;
+};
+
+//==============================================================================
+// __counted_by_or_null on struct non-pointer members
+//==============================================================================
+
+struct on_pod_ty {
+  int count;
+  // expected-error@+1{{'counted_by_or_null' only applies to pointers}}
+  int wrong_ty __counted_by_or_null(count);
+};
+
+struct on_void_ty {
+  int count;
+  // expected-error@+2{{'counted_by_or_null' only applies to pointers}}
+  // expected-error@+1{{field has incomplete type 'void'}}
+  void wrong_ty __counted_by_or_null(count);
+};
diff --git a/clang/test/Sema/attr-counted-by-or-null-vla-sizeless-types.c 
b/clang/test/Sema/attr-counted-by-or-null-vla-sizeless-types.c
new file mode 100644
index 0000000000000..23298bb4ec2f6
--- /dev/null
+++ b/clang/test/Sema/attr-counted-by-or-null-vla-sizeless-types.c
@@ -0,0 +1,11 @@
+// __SVInt8_t is specific to ARM64 so specify that in the target triple
+// RUN: %clang_cc1 -triple arm64-apple-darwin -fsyntax-only -verify %s
+
+#define __counted_by_or_null(f)  __attribute__((counted_by_or_null(f)))
+
+struct on_sizeless_elt_ty {
+    int count;
+    // expected-error@+2{{'counted_by_or_null' only applies to pointers}}
+    // expected-error@+1{{array has sizeless element type '__SVInt8_t'}}
+    __SVInt8_t arr[] __counted_by_or_null(count);
+};
diff --git a/clang/test/Sema/attr-sized-by-late-parsed-off.c 
b/clang/test/Sema/attr-sized-by-late-parsed-off.c
new file mode 100644
index 0000000000000..e43125c8ce2f9
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-late-parsed-off.c
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -DNEEDS_LATE_PARSING 
-fno-experimental-late-parse-attributes -fsyntax-only -verify %s
+// RUN: %clang_cc1 -DNEEDS_LATE_PARSING -fsyntax-only -verify %s
+
+// RUN: %clang_cc1 -UNEEDS_LATE_PARSING 
-fno-experimental-late-parse-attributes -fsyntax-only -verify=ok %s
+// RUN: %clang_cc1 -UNEEDS_LATE_PARSING -fsyntax-only -verify=ok %s
+
+#define __sized_by(f)  __attribute__((sized_by(f)))
+
+struct size_known { int dummy; };
+
+#ifdef NEEDS_LATE_PARSING
+struct on_decl {
+  // expected-error@+1{{use of undeclared identifier 'count'}}
+  struct size_known *buf __sized_by(count);
+  int count;
+};
+
+#else
+
+// ok-no-diagnostics
+struct on_decl {
+  int count;
+  struct size_known *buf __sized_by(count);
+};
+
+#endif
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
new file mode 100644
index 0000000000000..b58e6eeb2a02f
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
@@ -0,0 +1,243 @@
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify 
%s
+
+#define __sized_by(f)  __attribute__((sized_by(f)))
+
+struct size_unknown;
+struct size_known {
+  int field;
+};
+
+typedef void(*fn_ptr_ty)(void);
+
+//==============================================================================
+// __sized_by on struct member pointer in decl attribute position
+//==============================================================================
+
+struct on_member_pointer_complete_ty {
+  struct size_known * buf __sized_by(size);
+  int size;
+};
+
+struct on_member_pointer_incomplete_ty {
+  struct size_unknown * buf __sized_by(size);
+  int size;
+};
+
+struct on_member_pointer_const_incomplete_ty {
+  const struct size_unknown * buf __sized_by(size);
+  int size;
+};
+
+struct on_member_pointer_void_ty {
+  void* buf __sized_by(size);
+  int size;
+};
+
+struct on_member_pointer_fn_ptr_ty {
+  // buffer of `size` function pointers is allowed
+  void (**fn_ptr)(void) __sized_by(size);
+  int size;
+};
+
+
+struct on_member_pointer_fn_ptr_ty_ptr_ty {
+  // buffer of `size` function pointers is allowed
+  fn_ptr_ty* fn_ptr __sized_by(size);
+  int size;
+};
+
+struct on_member_pointer_fn_ty {
+  // buffer of function(s) with size `size` is allowed
+  void (*fn_ptr)(void) __sized_by(size);
+  int size;
+};
+
+struct on_member_pointer_fn_ptr_ty_ty {
+  // buffer of function(s) with size `size` is allowed
+  fn_ptr_ty fn_ptr __sized_by(size);
+  int size;
+};
+
+struct has_unannotated_vla {
+  int size;
+  int buffer[];
+};
+
+struct on_member_pointer_struct_with_vla {
+  struct has_unannotated_vla* objects __sized_by(size);
+  int size;
+};
+
+struct has_annotated_vla {
+  int size;
+  // expected-error@+1{{'sized_by' only applies to pointers}}
+  int buffer[] __sized_by(size);
+};
+
+struct on_member_pointer_struct_with_annotated_vla {
+  struct has_annotated_vla* objects __sized_by(size);
+  int size;
+};
+
+struct on_pointer_anon_buf {
+  // TODO: Support referring to parent scope
+  struct {
+    // expected-error@+1{{use of undeclared identifier 'size'}}
+    struct size_known *buf __sized_by(size);
+  };
+  int size;
+};
+
+struct on_pointer_anon_count {
+  struct size_known *buf __sized_by(size);
+  struct {
+    int size;
+  };
+};
+
+//==============================================================================
+// __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 {
+  // 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;
+};
+
+// -
+
+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'}}
+  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'}}
+  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'}}
+  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'}}
+  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'}}
+  struct has_annotated_vla* __sized_by(size) objects;
+  int size;
+};
+
+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'}}
+  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;
+};
+
+struct on_pointer_anon_buf_ty_pos {
+  struct {
+    // TODO: Support referring to parent scope
+    // expected-error@+1{{use of undeclared identifier 'size'}}
+    struct size_known * __sized_by(size) buf;
+  };
+  int size;
+};
+
+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;
+  };
+};
+
+//==============================================================================
+// __sized_by on struct non-pointer members
+//==============================================================================
+
+struct on_pod_ty {
+  // expected-error@+1{{'sized_by' only applies to pointers}}
+  int wrong_ty __sized_by(size);
+  int size;
+};
+
+struct on_void_ty {
+  // expected-error@+2{{'sized_by' only applies to pointers}}
+  // expected-error@+1{{field has incomplete type 'void'}}
+  void wrong_ty __sized_by(size);
+  int size;
+};
diff --git a/clang/test/Sema/attr-sized-by-or-null-late-parsed-off.c 
b/clang/test/Sema/attr-sized-by-or-null-late-parsed-off.c
new file mode 100644
index 0000000000000..8bc775f196c18
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-or-null-late-parsed-off.c
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -DNEEDS_LATE_PARSING 
-fno-experimental-late-parse-attributes -fsyntax-only -verify %s
+// RUN: %clang_cc1 -DNEEDS_LATE_PARSING -fsyntax-only -verify %s
+
+// RUN: %clang_cc1 -UNEEDS_LATE_PARSING 
-fno-experimental-late-parse-attributes -fsyntax-only -verify=ok %s
+// RUN: %clang_cc1 -UNEEDS_LATE_PARSING -fsyntax-only -verify=ok %s
+
+#define __sized_by_or_null(f)  __attribute__((sized_by_or_null(f)))
+
+struct size_known { int dummy; };
+
+#ifdef NEEDS_LATE_PARSING
+struct on_decl {
+  // expected-error@+1{{use of undeclared identifier 'count'}}
+  struct size_known *buf __sized_by_or_null(count);
+  int count;
+};
+
+#else
+
+// ok-no-diagnostics
+struct on_decl {
+  int count;
+  struct size_known *buf __sized_by_or_null(count);
+};
+
+#endif
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
new file mode 100644
index 0000000000000..0ddcd46c6cfb3
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
@@ -0,0 +1,243 @@
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify 
%s
+
+#define __sized_by_or_null(f)  __attribute__((__sized_by_or_null__(f)))
+
+struct size_unknown;
+struct size_known {
+  int field;
+};
+
+typedef void(*fn_ptr_ty)(void);
+
+//==============================================================================
+// __sized_by_or_null on struct member pointer in decl attribute position
+//==============================================================================
+
+struct on_member_pointer_complete_ty {
+  struct size_known * buf __sized_by_or_null(size);
+  int size;
+};
+
+struct on_member_pointer_incomplete_ty {
+  struct size_unknown * buf __sized_by_or_null(size);
+  int size;
+};
+
+struct on_member_pointer_const_incomplete_ty {
+  const struct size_unknown * buf __sized_by_or_null(size);
+  int size;
+};
+
+struct on_member_pointer_void_ty {
+  void* buf __sized_by_or_null(size);
+  int size;
+};
+
+struct on_member_pointer_fn_ptr_ty {
+  // buffer of `size` function pointers is allowed
+  void (**fn_ptr)(void) __sized_by_or_null(size);
+  int size;
+};
+
+
+struct on_member_pointer_fn_ptr_ty_ptr_ty {
+  // buffer of `size` function pointers is allowed
+  fn_ptr_ty* fn_ptr __sized_by_or_null(size);
+  int size;
+};
+
+struct on_member_pointer_fn_ty {
+  // buffer of function(s) with size `size` is allowed
+  void (*fn_ptr)(void) __sized_by_or_null(size);
+  int size;
+};
+
+struct on_member_pointer_fn_ptr_ty_ty {
+  // buffer of function(s) with size `size` is allowed
+  fn_ptr_ty fn_ptr __sized_by_or_null(size);
+  int size;
+};
+
+struct has_unannotated_vla {
+  int size;
+  int buffer[];
+};
+
+struct on_member_pointer_struct_with_vla {
+  struct has_unannotated_vla* objects __sized_by_or_null(size);
+  int size;
+};
+
+struct has_annotated_vla {
+  int size;
+  // expected-error@+1{{'sized_by_or_null' only applies to pointers}}
+  int buffer[] __sized_by_or_null(size);
+};
+
+struct on_member_pointer_struct_with_annotated_vla {
+  struct has_annotated_vla* objects __sized_by_or_null(size);
+  int size;
+};
+
+struct on_pointer_anon_buf {
+  // TODO: Support referring to parent scope
+  struct {
+    // expected-error@+1{{use of undeclared identifier 'size'}}
+    struct size_known *buf __sized_by_or_null(size);
+  };
+  int size;
+};
+
+struct on_pointer_anon_count {
+  struct size_known *buf __sized_by_or_null(size);
+  struct {
+    int 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 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;
+};
+
+// -
+
+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'}}
+  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'}}
+  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'}}
+  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'}}
+  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'}}
+  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'}}
+  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;
+};
+
+struct on_pointer_anon_buf_ty_pos {
+  struct {
+    // TODO: Support referring to parent scope
+    // expected-error@+1{{use of undeclared identifier 'size'}}
+    struct size_known * __sized_by_or_null(size) buf;
+  };
+  int size;
+};
+
+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;
+  };
+};
+
+//==============================================================================
+// __sized_by_or_null on struct non-pointer members
+//==============================================================================
+
+struct on_pod_ty {
+  // expected-error@+1{{'sized_by_or_null' only applies to pointers}}
+  int wrong_ty __sized_by_or_null(size);
+  int size;
+};
+
+struct on_void_ty {
+  // expected-error@+2{{'sized_by_or_null' only applies to pointers}}
+  // expected-error@+1{{field has incomplete type 'void'}}
+  void wrong_ty __sized_by_or_null(size);
+  int size;
+};
diff --git a/clang/test/Sema/attr-sized-by-or-null-struct-ptrs-sizeless-types.c 
b/clang/test/Sema/attr-sized-by-or-null-struct-ptrs-sizeless-types.c
new file mode 100644
index 0000000000000..af14038ab2575
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-or-null-struct-ptrs-sizeless-types.c
@@ -0,0 +1,16 @@
+// __SVInt8_t is specific to ARM64 so specify that in the target triple
+// RUN: %clang_cc1 -triple arm64-apple-darwin -fsyntax-only -verify %s
+
+#define __sized_by_or_null(f)  __attribute__((sized_by_or_null(f)))
+
+struct on_sizeless_pointee_ty {
+    int size;
+    __SVInt8_t* member __sized_by_or_null(size);
+};
+
+struct on_sizeless_ty {
+    int size;
+    // expected-error@+2{{'sized_by_or_null' only applies to pointers}}
+    // expected-error@+1{{field has sizeless type '__SVInt8_t'}}
+    __SVInt8_t member __sized_by_or_null(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
new file mode 100644
index 0000000000000..2dd963afad7a4
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
@@ -0,0 +1,211 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#define __sized_by_or_null(f)  __attribute__((sized_by_or_null(f)))
+#define __counted_by(f)  __attribute__((counted_by(f)))
+
+struct size_unknown;
+struct size_known {
+  int field;
+};
+
+typedef void(*fn_ptr_ty)(void);
+
+//==============================================================================
+// __sized_by_or_null on struct member pointer in decl attribute position
+//==============================================================================
+
+struct on_member_pointer_complete_ty {
+  int size;
+  struct size_known * buf __sized_by_or_null(size);
+};
+
+struct on_member_pointer_incomplete_ty {
+  int size;
+  struct size_unknown * buf __sized_by_or_null(size);
+};
+
+struct on_member_pointer_const_incomplete_ty {
+  int size;
+  const struct size_unknown * buf __sized_by_or_null(size);
+};
+
+struct on_member_pointer_void_ty {
+  int size;
+  void* buf __sized_by_or_null(size);
+};
+
+struct on_member_pointer_fn_ptr_ty {
+  int size;
+  // buffer of function pointers with size `size` is allowed
+  void (**fn_ptr)(void) __sized_by_or_null(size);
+};
+
+struct on_member_pointer_fn_ptr_ty_ptr_ty {
+  int size;
+  // buffer of function pointers with size `size` is allowed
+  fn_ptr_ty* fn_ptr __sized_by_or_null(size);
+};
+
+struct on_member_pointer_fn_ty {
+  int size;
+  // buffer of functions with size `size` is allowed
+  void (*fn_ptr)(void) __sized_by_or_null(size);
+};
+
+struct on_member_pointer_fn_ptr_ty_ty {
+  int size;
+  // buffer of functions with size `size` is allowed
+  fn_ptr_ty fn_ptr __sized_by_or_null(size);
+};
+
+struct has_unannotated_vla {
+  int count;
+  int buffer[];
+};
+
+struct on_member_pointer_struct_with_vla {
+  int size;
+  // we know the size so this is fine for tracking size, however indexing 
would be an issue
+  struct has_unannotated_vla* objects __sized_by_or_null(size);
+};
+
+struct has_annotated_vla {
+  int count;
+  int buffer[] __counted_by(count);
+};
+
+struct on_member_pointer_struct_with_annotated_vla {
+  int size;
+  // we know the size so this is fine for tracking size, however indexing 
would be an issue
+  struct has_annotated_vla* objects __sized_by_or_null(size);
+};
+
+struct on_pointer_anon_buf {
+  int size;
+  struct {
+    struct size_known *buf __sized_by_or_null(size);
+  };
+};
+
+struct on_pointer_anon_size {
+  struct {
+    int size;
+  };
+  struct size_known *buf __sized_by_or_null(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;
+  struct size_known *__sized_by_or_null(size) buf;
+};
+
+struct on_member_pointer_incomplete_ty_ty_pos {
+  int size;
+  struct size_unknown * __sized_by_or_null(size) buf;
+};
+
+struct on_member_pointer_const_incomplete_ty_ty_pos {
+  int size;
+  const struct size_unknown * __sized_by_or_null(size) buf;
+};
+
+struct on_member_pointer_void_ty_ty_pos {
+  int size;
+  void *__sized_by_or_null(size) buf;
+};
+
+// -
+
+struct on_member_pointer_fn_ptr_ty_pos {
+  int size;
+  // buffer of `size` function pointers is allowed
+  void (** __sized_by_or_null(size) fn_ptr)(void);
+};
+
+struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
+  int size;
+  // buffer of `size` function pointers is allowed
+  fn_ptr_ty* __sized_by_or_null(size) fn_ptr;
+};
+
+struct on_member_pointer_fn_ty_ty_pos {
+  int size;
+  void (* __sized_by_or_null(size) fn_ptr)(void);
+};
+
+struct on_member_pointer_fn_ptr_ty_ty_pos {
+  int size;
+  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;
+  void (* __sized_by_or_null(size) * fn_ptr)(void);
+};
+
+struct on_member_pointer_struct_with_vla_ty_pos {
+  int size;
+  struct has_unannotated_vla *__sized_by_or_null(size) objects;
+};
+
+struct on_member_pointer_struct_with_annotated_vla_ty_pos {
+  int size;
+  struct has_annotated_vla* __sized_by_or_null(size) objects;
+};
+
+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;
+  struct size_known *__sized_by_or_null(size) *buf;
+};
+
+struct on_nested_pointer_outer {
+  int size;
+  struct size_known **__sized_by_or_null(size) buf;
+};
+
+struct on_pointer_anon_buf_ty_pos {
+  int size;
+  struct {
+    struct size_known * __sized_by_or_null(size) buf;
+  };
+};
+
+struct on_pointer_anon_size_ty_pos {
+  struct {
+    int size;
+  };
+  struct size_known *__sized_by_or_null(size) buf;
+};
+
+//==============================================================================
+// __sized_by_or_null on struct non-pointer members
+//==============================================================================
+
+struct on_pod_ty {
+  int size;
+  // expected-error@+1{{'sized_by_or_null' only applies to pointers}}
+  int wrong_ty __sized_by_or_null(size);
+};
+
+struct on_void_ty {
+  int size;
+  // expected-error@+2{{'sized_by_or_null' only applies to pointers}}
+  // expected-error@+1{{field has incomplete type 'void'}}
+  void wrong_ty __sized_by_or_null(size);
+};
+
+struct on_member_array_complete_ty {
+  int size;
+  // expected-error@+1{{'sized_by_or_null' only applies to pointers}}
+  struct size_known array[] __sized_by_or_null(size);
+};
diff --git a/clang/test/Sema/attr-sized-by-or-null-vla-sizeless-types.c 
b/clang/test/Sema/attr-sized-by-or-null-vla-sizeless-types.c
new file mode 100644
index 0000000000000..ec6397e5cf9ac
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-or-null-vla-sizeless-types.c
@@ -0,0 +1,11 @@
+// __SVInt8_t is specific to ARM64 so specify that in the target triple
+// RUN: %clang_cc1 -triple arm64-apple-darwin -fsyntax-only -verify %s
+
+#define __sized_by_or_null(f)  __attribute__((sized_by_or_null(f)))
+
+struct on_sizeless_elt_ty {
+    int count;
+    // expected-error@+2{{'sized_by_or_null' only applies to pointers}}
+    // expected-error@+1{{array has sizeless element type '__SVInt8_t'}}
+    __SVInt8_t arr[] __sized_by_or_null(count);
+};
diff --git a/clang/test/Sema/attr-sized-by-struct-ptrs-sizeless-types.c 
b/clang/test/Sema/attr-sized-by-struct-ptrs-sizeless-types.c
new file mode 100644
index 0000000000000..3c66b497d6626
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-struct-ptrs-sizeless-types.c
@@ -0,0 +1,16 @@
+// __SVInt8_t is specific to ARM64 so specify that in the target triple
+// RUN: %clang_cc1 -triple arm64-apple-darwin -fsyntax-only -verify %s
+
+#define __sized_by(f)  __attribute__((sized_by(f)))
+
+struct on_sizeless_pointee_ty {
+    int count;
+    __SVInt8_t* member __sized_by(count);
+};
+
+struct on_sizeless_ty {
+    int count;
+    // expected-error@+2{{'sized_by' only applies to pointers}}
+    // expected-error@+1{{field has sizeless type '__SVInt8_t'}}
+    __SVInt8_t member __sized_by(count);
+};
diff --git a/clang/test/Sema/attr-sized-by-struct-ptrs.c 
b/clang/test/Sema/attr-sized-by-struct-ptrs.c
new file mode 100644
index 0000000000000..e155a9398758e
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-struct-ptrs.c
@@ -0,0 +1,211 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#define __sized_by(f)  __attribute__((sized_by(f)))
+#define __counted_by(f)  __attribute__((counted_by(f)))
+
+struct size_unknown;
+struct size_known {
+  int field;
+};
+
+typedef void(*fn_ptr_ty)(void);
+
+//==============================================================================
+// __sized_by on struct member pointer in decl attribute position
+//==============================================================================
+
+struct on_member_pointer_complete_ty {
+  int size;
+  struct size_known * buf __sized_by(size);
+};
+
+struct on_member_pointer_incomplete_ty {
+  int size;
+  struct size_unknown * buf __sized_by(size);
+};
+
+struct on_member_pointer_const_incomplete_ty {
+  int size;
+  const struct size_unknown * buf __sized_by(size);
+};
+
+struct on_member_pointer_void_ty {
+  int size;
+  void* buf __sized_by(size);
+};
+
+struct on_member_pointer_fn_ptr_ty {
+  int size;
+  // buffer of function pointers with size `size` is allowed
+  void (**fn_ptr)(void) __sized_by(size);
+};
+
+struct on_member_pointer_fn_ptr_ty_ptr_ty {
+  int size;
+  // buffer of function pointers with size `size` is allowed
+  fn_ptr_ty* fn_ptr __sized_by(size);
+};
+
+struct on_member_pointer_fn_ty {
+  int size;
+  // buffer of functions with size `size` is allowed
+  void (*fn_ptr)(void) __sized_by(size);
+};
+
+struct on_member_pointer_fn_ptr_ty_ty {
+  int size;
+  // buffer of functions with size `size` is allowed
+  fn_ptr_ty fn_ptr __sized_by(size);
+};
+
+struct has_unannotated_vla {
+  int count;
+  int buffer[];
+};
+
+struct on_member_pointer_struct_with_vla {
+  int size;
+  // we know the size so this is fine for tracking size, however indexing 
would be an issue
+  struct has_unannotated_vla* objects __sized_by(size);
+};
+
+struct has_annotated_vla {
+  int count;
+  int buffer[] __counted_by(count);
+};
+
+struct on_member_pointer_struct_with_annotated_vla {
+  int size;
+  // we know the size so this is fine for tracking size, however indexing 
would be an issue
+  struct has_annotated_vla* objects __sized_by(size);
+};
+
+struct on_pointer_anon_buf {
+  int size;
+  struct {
+    struct size_known *buf __sized_by(size);
+  };
+};
+
+struct on_pointer_anon_size {
+  struct {
+    int size;
+  };
+  struct size_known *buf __sized_by(size);
+};
+
+//==============================================================================
+// __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
+
+struct on_member_pointer_complete_ty_ty_pos {
+  int size;
+  struct size_known *__sized_by(size) buf;
+};
+
+struct on_member_pointer_incomplete_ty_ty_pos {
+  int size;
+  struct size_unknown * __sized_by(size) buf;
+};
+
+struct on_member_pointer_const_incomplete_ty_ty_pos {
+  int size;
+  const struct size_unknown * __sized_by(size) buf;
+};
+
+struct on_member_pointer_void_ty_ty_pos {
+  int size;
+  void *__sized_by(size) buf;
+};
+
+// -
+
+struct on_member_pointer_fn_ptr_ty_pos {
+  int size;
+  // buffer of `size` function pointers is allowed
+  void (** __sized_by(size) fn_ptr)(void);
+};
+
+struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
+  int size;
+  // buffer of `size` function pointers is allowed
+  fn_ptr_ty* __sized_by(size) fn_ptr;
+};
+
+struct on_member_pointer_fn_ty_ty_pos {
+  int size;
+  void (* __sized_by(size) fn_ptr)(void);
+};
+
+struct on_member_pointer_fn_ptr_ty_ty_pos {
+  int size;
+  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;
+  void (* __sized_by(size) * fn_ptr)(void);
+};
+
+struct on_member_pointer_struct_with_vla_ty_pos {
+  int size;
+  struct has_unannotated_vla *__sized_by(size) objects;
+};
+
+struct on_member_pointer_struct_with_annotated_vla_ty_pos {
+  int size;
+  struct has_annotated_vla* __sized_by(size) objects;
+};
+
+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;
+  struct size_known *__sized_by(size) *buf;
+};
+
+struct on_nested_pointer_outer {
+  int size;
+  struct size_known **__sized_by(size) buf;
+};
+
+struct on_pointer_anon_buf_ty_pos {
+  int size;
+  struct {
+    struct size_known * __sized_by(size) buf;
+  };
+};
+
+struct on_pointer_anon_size_ty_pos {
+  struct {
+    int size;
+  };
+  struct size_known *__sized_by(size) buf;
+};
+
+//==============================================================================
+// __sized_by on struct non-pointer members
+//==============================================================================
+
+struct on_pod_ty {
+  int size;
+  // expected-error@+1{{'sized_by' only applies to pointers}}
+  int wrong_ty __sized_by(size);
+};
+
+struct on_void_ty {
+  int size;
+  // expected-error@+2{{'sized_by' only applies to pointers}}
+  // expected-error@+1{{field has incomplete type 'void'}}
+  void wrong_ty __sized_by(size);
+};
+
+struct on_member_array_complete_ty {
+  int size;
+  // expected-error@+1{{'sized_by' only applies to pointers}}
+  struct size_known array[] __sized_by(size);
+};
diff --git a/clang/test/Sema/attr-sized-by-vla-sizeless-types.c 
b/clang/test/Sema/attr-sized-by-vla-sizeless-types.c
new file mode 100644
index 0000000000000..835cf035f2e06
--- /dev/null
+++ b/clang/test/Sema/attr-sized-by-vla-sizeless-types.c
@@ -0,0 +1,11 @@
+// __SVInt8_t is specific to ARM64 so specify that in the target triple
+// RUN: %clang_cc1 -triple arm64-apple-darwin -fsyntax-only -verify %s
+
+#define __sized_by(f)  __attribute__((sized_by(f)))
+
+struct on_sizeless_elt_ty {
+    int count;
+    // expected-error@+2{{'sized_by' only applies to pointers}}
+    // expected-error@+1{{array has sizeless element type '__SVInt8_t'}}
+    __SVInt8_t arr[] __sized_by(count);
+};

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to