https://github.com/delcypher updated 
https://github.com/llvm/llvm-project/pull/106321

>From f5b291e55859265c227427d0d630f8f698320a84 Mon Sep 17 00:00:00 2001
From: Dan Liew <d...@su-root.co.uk>
Date: Thu, 15 Aug 2024 16:33:04 -0700
Subject: [PATCH] [BoundsSafety][Sema] Allow counted_by and counted_by_or_null
 on pointers where the pointee type is incomplete but potentially completable

Previously using the `counted_by` or `counted_by_or_null` attribute on a
pointer with an incomplete pointee type was forbidden. Unfortunately
this prevented a situation like the following from being allowed.

Header file:

```
struct EltTy; // Incomplete type
struct Buffer {
  size_t count;
  struct EltTy* __counted_by(count) buffer; // error before this patch
};
```

Implementation file:

```
struct EltTy {
  // definition
};

void allocBuffer(struct Buffer* b) {
  b->buffer = malloc(sizeof(EltTy)* b->count);
}
```

To allow code like the above but still enforce that the pointee
type size is known in locations where `-fbounds-safety` needs to
emit bounds checks the following scheme is used.

* For incomplete pointee types that can never be completed (e.g. `void`)
  these are treated as error where the attribute is written (just like
  before this patch).
* For incomplete pointee types that might be completable later on
  (struct, union, and enum forward declarations)
  in the translation unit, writing the attribute on the incomplete
  pointee type is allowed on the FieldDecl declaration but "uses" of the
  declared pointer are forbidden if at the point of "use" the pointee
  type is still incomplete.

For this patch a "use" of a FieldDecl covers:

* Explicit and Implicit initialization (note see **Tentative Definition
  Initialization** for an exception to this)
* Assignment
* Conversion to an lvalue (e.g. for use in an expression)

In Apple's internal fork of Clang the `counted_by` and
`counted_by_or_null` attribute are allowed in many more contexts. That
isn't the case for upstream Clang so the "use" checks for the attribute
on VarDecl, ParamVarDecl, and function return type have been omitted
from this patch because they can't be tested. However, the
`BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy` and
`BoundsSafetyCheckUseOfCountAttrPtrWithIncompletePointeeTy` functions
retain the ability to emit diagnostics for these other contexts to avoid
unnecessary divergence between upstream Clang and Apple's internal fork.
Support for checking "uses" will be upstreamed when upstream Clang
allows the `counted_by` and `counted_by_or_null` attribute in additional
contexts.

This patch also includes the new `Sema::BoundsSafetySourceRangeFor`
method which allows a more accurate SourceRange for the `counted_by`
and `counted_by_or_null` attributes to be shown in diagnostics. This
is used by the new `diag::note_named_attribute` diagnostic introduced in
this patch. This method has several shortcomings but it didn't seem
worth covering them given this is essentially a workaround for the
fact `CountAttributedType` doesn't store the SourceLocation for its
attribute.

This patch has a few limitations:

** 1. Tentative Defition Initialization **

This patch currently allows something like:

```
struct IncompleteTy;
struct Buffer {
  int count;
  struct IncompleteTy* __counted_by(count) buf;
};

// Tentative definition
struct Buffer GlobalBuf;
```

The Tentative definition in this example becomes an actual definition
whose initialization **should be checked** but it currently isn't.
Addressing this problem will be done in a subseqent patch.

** 2. When the incomplete pointee type is a typedef diagnostics are slightly 
misleading **

For this situation:

```

struct IncompleteTy;
typedef struct IncompleteTy Incomplete_t;

struct Buffer {
  int count;
  struct IncompleteTy* __counted_by(count) buf;
};

void use(struct Buffer b) {
  b.buf = 0x0;
}
```

This code emits `note: forward declaration of 'Incomplete_t' (aka
'struct IncompleteTy')` but the location is on the `struct
IncompleteTy;` forward declaration.  This is misleading because
`Incomplete_t` isn't actually forward declared there (instead the
underlying type is). This could be resolved by additional diagnostics
that walk the chain of typedefs and explain each step of the walk.
However, that would be very verbose and didn't seem like a direction
worth pursuing.

rdar://133600117
---
 clang/include/clang/AST/Type.h                |  22 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  37 ++
 clang/include/clang/Sema/Sema.h               |  44 ++
 clang/lib/AST/Type.cpp                        |  41 ++
 clang/lib/Sema/SemaBoundsSafety.cpp           | 218 +++++-
 clang/lib/Sema/SemaExpr.cpp                   |  23 +
 clang/lib/Sema/SemaInit.cpp                   |  13 +
 .../attr-counted-by-late-parsed-struct-ptrs.c |   5 +-
 .../Sema/attr-counted-by-or-null-last-field.c |   4 +-
 ...unted-by-or-null-late-parsed-struct-ptrs.c |   5 +-
 .../attr-counted-by-or-null-struct-ptrs.c     |  12 +-
 ...ruct-ptrs-completable-incomplete-pointee.c | 622 ++++++++++++++++++
 clang/test/Sema/attr-counted-by-struct-ptrs.c |  12 +-
 clang/test/Sema/attr-counted-by-vla.c         |   3 +-
 14 files changed, 1034 insertions(+), 27 deletions(-)
 create mode 100644 
clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c

diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 9f6189440fabf..0d96262beb785 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2433,6 +2433,26 @@ class alignas(TypeAlignment) Type : public 
ExtQualsTypeCommonBase {
     return !isFunctionType();
   }
 
+  /// \returns True if the type is incomplete and it is also a type that
+  /// cannot be completed by a later type definition.
+  ///
+  /// E.g. For `void` this is true but for `struct ForwardDecl;` this is false
+  /// because a definition for `ForwardDecl` could be provided later on in the
+  /// translation unit.
+  ///
+  /// Note even for types that this function returns true for it is still
+  /// possible for the declarations that contain this type to later have a
+  /// complete type in a translation unit. E.g.:
+  ///
+  /// \code{.c}
+  /// // This decl has type 'char[]' which is incomplete and cannot be later
+  /// // completed by another by another type declaration.
+  /// extern char foo[];
+  /// // This decl now has complete type 'char[5]'.
+  /// char foo[5]; // foo has a complete type
+  /// \endcode
+  bool isAlwaysIncompleteType() const;
+
   /// Determine whether this type is an object type.
   bool isObjectType() const {
     // C++ [basic.types]p8:
@@ -3349,6 +3369,8 @@ class CountAttributedType final
   static bool classof(const Type *T) {
     return T->getTypeClass() == CountAttributed;
   }
+
+  StringRef getAttributeName(bool WithMacroPrefix) const;
 };
 
 /// Represents a type which was implicitly adjusted by the semantic
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1ad09aba60935..b7425431f18f3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4445,6 +4445,7 @@ def err_mismatched_visibility: Error<"visibility does not 
match previous declara
 def note_previous_attribute : Note<"previous attribute is here">;
 def note_conflicting_attribute : Note<"conflicting attribute is here">;
 def note_attribute : Note<"attribute is here">;
+def note_named_attribute : Note<"%0 attribute is here">;
 def err_mismatched_ms_inheritance : Error<
   "inheritance model does not match %select{definition|previous 
declaration}0">;
 def warn_ignored_ms_inheritance : Warning<
@@ -6710,6 +6711,42 @@ def err_counted_by_attr_pointee_unknown_size : Error<
     "%select{|. This will be an error in a future compiler version}3"
     ""
   "}2">;
+def err_counted_by_on_incomplete_type_on_assign : Error <
+  "cannot %select{"
+    "assign to %select{object|'%1'}2 that has|" // AA_Assigning,
+    "pass argument to %select{parameter|parameter '%1'}2 that has|" // 
AA_Passing,
+    "return|" // AA_Returning,
+    "convert to|" // AA_Converting (UNUSED)
+    "%select{|implicitly }3initialize %select{object|'%1'}2 that has|" // 
AA_Initializing,
+    "pass argument to parameter that has|" // AA_Sending (UNUSED)
+    "cast to|" // AA_Casting (UNUSED)
+    "pass argument to parameter that has" // AA_Passing_CFAudited (UNUSED)
+  "}0 type %4 because the pointee type %6 is incomplete and the '%5' attribute 
"
+  "requires the pointee type be complete when %select{"
+    "assigning|" // AA_Assigning,
+    "passing|" // AA_Passing,
+    "returning|" // AA_Returning,
+    "converting|" // AA_Converting (UNUSED)
+    "initializing|" // AA_Initializing,
+    "passing|" // AA_Sending (UNUSED)
+    "casting|" // AA_Casting (UNUSED)
+    "passing" // AA_Passing_CFAudited (UNUSED)
+  "}0; "
+  "consider providing a complete definition for %6 or using the "
+  "'__sized_by%select{|_or_null}7' attribute"
+>;
+def err_counted_by_on_incomplete_type_on_use : Error <
+  "cannot %select{"
+    "use '%1'|" // Generic expr
+    "call '%1'" // CallExpr
+  "}0 with%select{"
+    "|"       // Generic expr
+    " return" // CallExpr
+  "}0 type %2 because the pointee type %3 is incomplete "
+  "and the '%4' attribute requires the pointee type be complete in this 
context; "
+  "consider providing a complete definition for %3 or using the "
+  "'__sized_by%select{|_or_null}5' attribute"
+>;
 
 def warn_counted_by_attr_elt_type_unknown_size :
   Warning<err_counted_by_attr_pointee_unknown_size.Summary>,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7bd77d33a1f3d..59659960da163 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2098,6 +2098,50 @@ class Sema final : public SemaBase {
   bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
                                  bool OrNull);
 
+  /// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` 
or
+  /// `__counted_by_or_null` pointer type \param LHSTy.
+  ///
+  /// \param LHSTy The type being assigned to. Checks will only be performed if
+  ///              the type is a `counted_by` or `counted_by_or_null ` pointer.
+  /// \param RHSExpr The expression being assigned from.
+  /// \param Action The type assignment being performed
+  /// \param Loc The SourceLocation to use for error diagnostics
+  /// \param Assignee The ValueDecl being assigned. This is used to compute
+  ///        the name of the assignee. If the assignee isn't known this can
+  ///        be set to nullptr.
+  /// \param ShowFullyQualifiedAssigneeName If set to true when using \p
+  ///        Assignee to compute the name of the assignee use the fully
+  ///        qualified name, otherwise use the unqualified name.
+  ///
+  /// \returns True iff no diagnostic where emitted, false otherwise.
+  bool BoundsSafetyCheckAssignmentToCountAttrPtr(
+      QualType LHSTy, Expr *RHSExpr, AssignmentAction Action,
+      SourceLocation Loc, const ValueDecl *Assignee,
+      bool ShowFullyQualifiedAssigneeName);
+
+  /// Perform Bounds Safety Semantic checks for initializing a Bounds Safety
+  /// pointer.
+  ///
+  /// \param Entity The entity being initialized
+  /// \param Kind The kind of initialization being performed
+  /// \param Action The type assignment being performed
+  /// \param LHSTy The type being assigned to. Checks will only be performed if
+  ///              the type is a `counted_by` or `counted_by_or_null ` pointer.
+  /// \param RHSExpr The expression being used for initialization.
+  ///
+  /// \returns True iff no diagnostic where emitted, false otherwise.
+  bool BoundsSafetyCheckInitialization(const InitializedEntity &Entity,
+                                       const InitializationKind &Kind,
+                                       AssignmentAction Action,
+                                       QualType LHSType, Expr *RHSExpr);
+
+  /// Perform Bounds Safety semantic checks for uses of invalid uses counted_by
+  /// or counted_by_or_null pointers in \param E.
+  ///
+  /// \param E the expression to check
+  ///
+  /// \returns True iff no diagnostic where emitted, false otherwise.
+  bool BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E);
   ///@}
 
   //
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 4336fe44b82ad..1dbad2e55191f 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2490,6 +2490,22 @@ bool Type::isIncompleteType(NamedDecl **Def) const {
   }
 }
 
+bool Type::isAlwaysIncompleteType() const {
+  if (!isIncompleteType())
+    return false;
+
+  // Forward declarations of structs, classes, enums, and unions could be later
+  // completed in a compilation unit by providing a type definition.
+  if (getAsTagDecl())
+    return false;
+
+  // Other types are incompletable.
+  //
+  // E.g. `char[]` and `void`. The type is incomplete and no future
+  // type declarations can make the type complete.
+  return true;
+}
+
 bool Type::isSizelessBuiltinType() const {
   if (isSizelessVectorType())
     return true;
@@ -3931,6 +3947,31 @@ CountAttributedType::CountAttributedType(
     DeclSlot[i] = CoupledDecls[i];
 }
 
+StringRef CountAttributedType::getAttributeName(bool WithMacroPrefix) const {
+// TODO: This method isn't really ideal because it doesn't return the spelling
+// of the attribute that was used in the user's code. This method is used for
+// diagnostics so the fact it doesn't use the spelling of the attribute in
+// the user's code could be confusing (#113585).
+#define ENUMERATE_ATTRS(PREFIX)                                                
\
+  do {                                                                         
\
+    if (isCountInBytes()) {                                                    
\
+      if (isOrNull())                                                          
\
+        return PREFIX "sized_by_or_null";                                      
\
+      return PREFIX "sized_by";                                                
\
+    }                                                                          
\
+    if (isOrNull())                                                            
\
+      return PREFIX "counted_by_or_null";                                      
\
+    return PREFIX "counted_by";                                                
\
+  } while (0)
+
+  if (WithMacroPrefix)
+    ENUMERATE_ATTRS("__");
+  else
+    ENUMERATE_ATTRS("");
+
+#undef ENUMERATE_ATTRS
+}
+
 TypedefType::TypedefType(TypeClass tc, const TypedefNameDecl *D,
                          QualType Underlying, QualType can)
     : Type(tc, can, toSemanticDependence(can->getDependence())),
diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp 
b/clang/lib/Sema/SemaBoundsSafety.cpp
index d63a2389ea11d..d433f13693963 100644
--- a/clang/lib/Sema/SemaBoundsSafety.cpp
+++ b/clang/lib/Sema/SemaBoundsSafety.cpp
@@ -11,6 +11,9 @@
 /// (e.g. `counted_by`)
 ///
 
//===----------------------------------------------------------------------===//
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Sema/Initialization.h"
 #include "clang/Sema/Sema.h"
 
 namespace clang {
@@ -102,7 +105,36 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr 
*E, bool CountInBytes,
   // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
   // when `FieldTy->isArrayType()`.
   bool ShouldWarn = false;
-  if (PointeeTy->isIncompleteType() && !CountInBytes) {
+  if (PointeeTy->isAlwaysIncompleteType() && !CountInBytes) {
+    // In general using `counted_by` or `counted_by_or_null` on
+    // pointers where the pointee is an incomplete type are problematic. This 
is
+    // because it isn't possible to compute the pointer's bounds without 
knowing
+    // the pointee type size. At the same time it is common to forward declare
+    // types in header files.
+    //
+    // E.g.:
+    //
+    // struct Handle;
+    // struct Wrapper {
+    //   size_t size;
+    //   struct Handle* __counted_by(count) handles;
+    // }
+    //
+    // To allow the above code pattern but still prevent the pointee type from
+    // being incomplete in places where bounds checks are needed the following
+    // scheme is used:
+    //
+    // * When the pointee type might not always be an incomplete type (i.e.
+    // a type that is currently incomplete but might be completed later
+    // on in the translation unit) the attribute is allowed by this method
+    // but later uses of the FieldDecl are checked that the pointee type
+    // is complete see `BoundsSafetyCheckAssignmentToCountAttrPtr`,
+    // `BoundsSafetyCheckInitialization`, and
+    // `BoundsSafetyCheckUseOfCountAttrPtr`
+    //
+    // * When the pointee type is always an incomplete type (e.g.
+    // `void`) the attribute is disallowed by this method because we know the
+    // type can never be completed so there's no reason to allow it.
     InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
   } else if (PointeeTy->isSizelessType()) {
     InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
@@ -186,4 +218,188 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr 
*E, bool CountInBytes,
   return false;
 }
 
+static void EmitIncompleteCountedByPointeeNotes(Sema &S,
+                                                const CountAttributedType 
*CATy,
+                                                NamedDecl *IncompleteTyDecl,
+                                                bool NoteAttrLocation = true) {
+  assert(IncompleteTyDecl == nullptr || isa<TypeDecl>(IncompleteTyDecl));
+
+  if (NoteAttrLocation) {
+    // Note where the attribute is declared
+    // This is an approximation that's not quite right. This points to the
+    // the expression inside the attribute rather than the attribute itself.
+    //
+    // TODO: Implement logic to find the relevant TypeLoc for the attribute and
+    // get the SourceRange from that (#113582).
+    SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange();
+    S.Diag(AttrSrcRange.getBegin(), diag::note_named_attribute)
+        << CATy->getAttributeName(/*WithMacroPrefix=*/true) << AttrSrcRange;
+  }
+
+  if (!IncompleteTyDecl)
+    return;
+
+  // If there's an associated forward declaration display it to emphasize
+  // why the type is incomplete (all we have is a forward declaration).
+
+  // Note the `IncompleteTyDecl` type is the underlying type which might not
+  // be the same as `CATy->getPointeeType()` which could be a typedef.
+  //
+  // The diagnostic printed will be at the location of the underlying type but
+  // the diagnostic text will print the type of `CATy->getPointeeType()` which
+  // could be a typedef name rather than the underlying type. This is ok
+  // though because the diagnostic will print the underlying type name too.
+  // E.g:
+  //
+  // `forward declaration of 'Incomplete_Struct_t'
+  //  (aka 'struct IncompleteStructTy')`
+  S.Diag(IncompleteTyDecl->getBeginLoc(), diag::note_forward_declaration)
+      << CATy->getPointeeType();
+}
+
+static std::tuple<const CountAttributedType *, QualType>
+HasCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND) {
+  auto *CATy = Ty->getAs<CountAttributedType>();
+  if (!CATy)
+    return {};
+
+  // Incomplete pointee type is only a problem for
+  // counted_by/counted_by_or_null
+  if (CATy->isCountInBytes())
+    return {};
+
+  auto PointeeTy = CATy->getPointeeType();
+  if (PointeeTy.isNull())
+    return {}; // Reachable?
+
+  if (!PointeeTy->isIncompleteType(ND))
+    return {};
+
+  return {CATy, PointeeTy};
+}
+
+/// Perform Checks for assigning to a `__counted_by` or
+/// `__counted_by_or_null` pointer type \param LHSTy where the pointee type
+/// is incomplete which is invalid.
+///
+/// \param S The Sema instance.
+/// \param LHSTy The type being assigned to. Checks will only be performed if
+///              the type is a `counted_by` or `counted_by_or_null ` pointer.
+/// \param RHSExpr The expression being assigned from.
+/// \param Action The type assignment being performed
+/// \param Loc The SourceLocation to use for error diagnostics
+/// \param Assignee The ValueDecl being assigned. This is used to compute
+///        the name of the assignee. If the assignee isn't known this can
+///        be set to nullptr.
+/// \param ShowFullyQualifiedAssigneeName If set to true when using \p
+///        Assignee to compute the name of the assignee use the fully
+///        qualified name, otherwise use the unqualified name.
+///
+/// \returns True iff no diagnostic where emitted, false otherwise.
+static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
+    Sema &S, QualType LHSTy, Expr *RHSExpr, AssignmentAction Action,
+    SourceLocation Loc, const ValueDecl *Assignee,
+    bool ShowFullyQualifiedAssigneeName) {
+  NamedDecl *IncompleteTyDecl = nullptr;
+  auto [CATy, PointeeTy] =
+      HasCountedByAttrOnIncompletePointee(LHSTy, &IncompleteTyDecl);
+  if (!CATy)
+    return true;
+
+  std::string AssigneeStr;
+  if (Assignee) {
+    if (ShowFullyQualifiedAssigneeName) {
+      AssigneeStr = Assignee->getQualifiedNameAsString();
+    } else {
+      AssigneeStr = Assignee->getNameAsString();
+    }
+  }
+  {
+    auto D = S.Diag(Loc, diag::err_counted_by_on_incomplete_type_on_assign)
+             << static_cast<int>(Action) << AssigneeStr
+             << (AssigneeStr.size() > 0) << isa<ImplicitValueInitExpr>(RHSExpr)
+             << LHSTy << CATy->getAttributeName(/*WithMacroPrefix=*/true)
+             << PointeeTy << CATy->isOrNull();
+
+    if (RHSExpr->getSourceRange().isValid())
+      D << RHSExpr->getSourceRange();
+  }
+
+  EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl);
+  return false; // check failed
+}
+
+bool Sema::BoundsSafetyCheckAssignmentToCountAttrPtr(
+    QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc,
+    const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName) {
+  return CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
+      *this, LHSTy, RHSExpr, Action, Loc, Assignee,
+      ShowFullyQualifiedAssigneeName);
+}
+
+bool Sema::BoundsSafetyCheckInitialization(const InitializedEntity &Entity,
+                                           const InitializationKind &Kind,
+                                           AssignmentAction Action,
+                                           QualType LHSType, Expr *RHSExpr) {
+  auto SL = Kind.getLocation();
+
+  // Note: We don't call `BoundsSafetyCheckAssignmentToCountAttrPtr` here
+  // because we need conditionalize what is checked. In downstream
+  // Clang `counted_by` is supported on variable definitions and in that
+  // implementation an error diagnostic will be emitted on the variable
+  // definition if the pointee is an incomplete type. To avoid warning about 
the
+  // same problem twice (once when the variable is defined, once when Sema
+  // checks the initializer) we skip checking the initializer if it's a
+  // variable.
+  if (Action == AssignmentAction::Initializing &&
+      Entity.getKind() != InitializedEntity::EK_Variable) {
+
+    if (!CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
+            *this, LHSType, RHSExpr, Action, SL,
+            dyn_cast_or_null<ValueDecl>(Entity.getDecl()),
+            /*ShowFullQualifiedAssigneeName=*/true)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E) {
+  QualType T = E->getType();
+  if (!T->isPointerType())
+    return true;
+
+  NamedDecl *IncompleteTyDecl = nullptr;
+  auto [CATy, PointeeTy] =
+      HasCountedByAttrOnIncompletePointee(T, &IncompleteTyDecl);
+  if (!CATy)
+    return true;
+
+  // Generate a string for the diagnostic that describes the "use".
+  // The string is specialized for direct calls to produce a better
+  // diagnostic.
+  SmallString<64> UseStr;
+  bool IsDirectCall = false;
+  if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParens())) {
+    if (const auto *FD = CE->getDirectCallee()) {
+      UseStr = FD->getName();
+      IsDirectCall = true;
+    }
+  }
+
+  if (!IsDirectCall) {
+    llvm::raw_svector_ostream SS(UseStr);
+    E->printPretty(SS, nullptr, getPrintingPolicy());
+  }
+
+  Diag(E->getBeginLoc(), diag::err_counted_by_on_incomplete_type_on_use)
+      << IsDirectCall << UseStr << T << PointeeTy
+      << CATy->getAttributeName(/*WithMacroPrefix=*/true) << CATy->isOrNull()
+      << E->getSourceRange();
+
+  EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl);
+  return false;
+}
+
 } // namespace clang
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e7f418ae6802e..13f3c7f377b50 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -732,6 +732,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
   if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct)
     Cleanup.setExprNeedsCleanups(true);
 
+  if (!BoundsSafetyCheckUseOfCountAttrPtr(Res.get()))
+    return ExprError();
+
   // C++ [conv.lval]p3:
   //   If T is cv std::nullptr_t, the result is a null pointer constant.
   CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue;
@@ -13806,6 +13809,26 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, 
ExprResult &RHS,
   QualType LHSType = LHSExpr->getType();
   QualType RHSType = CompoundType.isNull() ? RHS.get()->getType() :
                                              CompoundType;
+
+  if (RHS.isUsable()) {
+    // Even if this check fails don't return early to allow the best
+    // possible error recovery and to allow any subsequent diagnostics to
+    // work.
+    const ValueDecl *Assignee = nullptr;
+    bool ShowFullyQualifiedAssigneeName = false;
+    // In simple cases describe what is being assigned to
+    if (auto *DR = dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenCasts())) {
+      Assignee = DR->getDecl();
+    } else if (auto *ME = dyn_cast<MemberExpr>(LHSExpr->IgnoreParenCasts())) {
+      Assignee = ME->getMemberDecl();
+      ShowFullyQualifiedAssigneeName = true;
+    }
+
+    BoundsSafetyCheckAssignmentToCountAttrPtr(
+        LHSType, RHS.get(), AssignmentAction::Assigning, Loc, Assignee,
+        ShowFullyQualifiedAssigneeName);
+  }
+
   // OpenCL v1.2 s6.1.1.1 p2:
   // The half data type can only be used to declare a pointer to a buffer that
   // contains half values
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 87a4244f2fb76..dba9c28416b63 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8314,6 +8314,12 @@ ExprResult InitializationSequence::Perform(Sema &S,
             Kind.getRange().getEnd());
       } else {
         CurInit = new (S.Context) ImplicitValueInitExpr(Step->Type);
+        // Note the return value isn't used to return a ExprError() when
+        // initialization fails . For struct initialization allows all field
+        // assignments to be checked rather than bailing on the first error.
+        S.BoundsSafetyCheckInitialization(Entity, Kind,
+                                          AssignmentAction::Initializing,
+                                          Step->Type, CurInit.get());
       }
       break;
     }
@@ -8360,6 +8366,13 @@ ExprResult InitializationSequence::Perform(Sema &S,
         }
       }
 
+      // Note the return value isn't used to return a ExprError() when
+      // initialization fails. For struct initialization this allows all field
+      // assignments to be checked rather than bailing on the first error.
+      S.BoundsSafetyCheckInitialization(Entity, Kind,
+                                        getAssignmentAction(Entity, true),
+                                        Step->Type, InitialCurInit.get());
+
       bool Complained;
       if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(),
                                      Step->Type, SourceType,
diff --git a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c 
b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
index 9ff3b080f6576..8d4e0c510603a 100644
--- a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
@@ -19,13 +19,12 @@ struct on_member_pointer_complete_ty {
 };
 
 struct on_member_pointer_incomplete_ty {
-  struct size_unknown * buf __counted_by(count); // 
expected-error{{'counted_by' 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(count); // ok
   int count;
 };
 
 struct on_member_pointer_const_incomplete_ty {
-  // expected-error@+1{{'counted_by' 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(count);
+  const struct size_unknown * buf __counted_by(count); // ok
   int count;
 };
 
diff --git a/clang/test/Sema/attr-counted-by-or-null-last-field.c 
b/clang/test/Sema/attr-counted-by-or-null-last-field.c
index 12c0b6de44f72..60a1f571b19e9 100644
--- a/clang/test/Sema/attr-counted-by-or-null-last-field.c
+++ b/clang/test/Sema/attr-counted-by-or-null-last-field.c
@@ -118,12 +118,12 @@ struct annotated_with_anon_struct {
 struct count_unknown;
 struct on_member_ptr_incomplete_ty_ty_pos {
   int count;
-  struct count_unknown * ptr __counted_by_or_null(count); // expected-error 
{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown 
size because 'struct count_unknown' is an incomplete type}}
+  struct count_unknown * ptr __counted_by_or_null(count); // ok
 };
 
 struct on_member_ptr_incomplete_const_ty_ty_pos {
   int count;
-  const struct count_unknown * ptr __counted_by_or_null(count); // 
expected-error {{'counted_by_or_null' cannot be applied to a pointer with 
pointee of unknown size because 'const struct count_unknown' is an incomplete 
type}}
+  const struct count_unknown * ptr __counted_by_or_null(count); // ok
 };
 
 struct on_member_ptr_void_ty_ty_pos {
diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c 
b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
index 95f517e3144f7..2150c81f9e9be 100644
--- a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
@@ -20,13 +20,12 @@ struct on_member_pointer_complete_ty {
 };
 
 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}}
+  struct size_unknown * buf __counted_by_or_null(count); // ok
   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);
+  const struct size_unknown * buf __counted_by_or_null(count); // ok
   int 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
index 708bb727ce09d..0bb09059c97f9 100644
--- a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
@@ -22,14 +22,12 @@ struct on_member_pointer_complete_ty {
 
 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 size_unknown * buf __counted_by_or_null(count); // ok
 };
 
 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);
+  const struct size_unknown * buf __counted_by_or_null(count); // ok
 };
 
 struct on_member_pointer_void_ty {
@@ -116,14 +114,12 @@ struct on_member_pointer_complete_ty_ty_pos {
 
 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 size_unknown * __counted_by_or_null(count) buf; // ok
 };
 
 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;
+  const struct size_unknown * __counted_by_or_null(count) buf; // ok
 };
 
 struct on_member_pointer_void_ty_ty_pos {
diff --git 
a/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c 
b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
new file mode 100644
index 0000000000000..746e62e0bb207
--- /dev/null
+++ 
b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
@@ -0,0 +1,622 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify 
%s
+
+#define __counted_by(f)  __attribute__((counted_by(f)))
+
+// 
=============================================================================
+// # Struct incomplete type with attribute in the decl position
+// 
=============================================================================
+
+// Note: This could be considered misleading. The typedef isn't actually on 
this
+// line. Also note the discrepancy in diagnostic count (27 vs 51) is due to
+// the pointer arithmetic on incomplete pointee type diagnostic always using
+// diagnostic text that refers to the underlying forward decl, even when the
+// typedef is used.
+// expected-note@+1 27{{forward declaration of 'Incomplete_t' (aka 'struct 
IncompleteTy')}}
+struct IncompleteTy; // expected-note 51{{forward declaration of 'struct 
IncompleteTy'}}
+
+typedef struct IncompleteTy Incomplete_t; 
+
+struct CBBufDeclPos {
+  int count;
+  struct IncompleteTy* buf __counted_by(count); // OK expected-note 
27{{__counted_by attribute is here}}
+  Incomplete_t* buf_typedef __counted_by(count); // OK expected-note 
27{{__counted_by attribute is here}}
+};
+
+void consume_struct_IncompleteTy(struct IncompleteTy* buf);
+
+int idx(void);
+
+
+
+void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
+  // 
===========================================================================
+  // ## Local variable initialization
+  // 
===========================================================================
+  struct CBBufDeclPos explicit_desig_init = {
+    .count = 0,
+    // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' that has type 
'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy' or using the '__sized_by' attribute}}
+    .buf = 0x0,
+    // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' that 
has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete when initializing; consider providing a complete definition for 
'Incomplete_t' or using the '__sized_by' attribute}}
+    .buf_typedef = 0x0
+  };
+  // Variable is not currently marked as invalid so uses of the variable allows
+  // diagnostics to fire.
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 
'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'struct IncompleteTy' or using the 
'__sized_by' attribute}}
+  explicit_desig_init.buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has 
type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because 
the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and 
the '__counted_by' attribute requires the pointee type be complete when 
assigning; consider providing a complete definition for 'Incomplete_t' or using 
the '__sized_by' attribute}}
+  explicit_desig_init.buf_typedef = 0x0;
+  // expected-error@+1{{cannot use 'explicit_desig_init.buf' with type 'struct 
IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' 
attribute requires the pointee type be complete in this context; consider 
providing a complete definition for 'struct IncompleteTy' or using the 
'__sized_by' attribute}}
+  void *tmp = explicit_desig_init.buf;
+  // expected-error@+1{{cannot use 'explicit_desig_init.buf_typedef' with type 
'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'Incomplete_t' or using the 
'__sized_by' attribute}}
+  void *tmp2 = explicit_desig_init.buf_typedef;
+
+  struct CBBufDeclPos partial_explicit_desig_init = {
+    .count = 0,
+    // .buf and .buf_typedef are implicit zero initialized
+    // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' 
that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct 
IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete 
and the '__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy' or using the '__sized_by' attribute}}
+    // expected-error@+1{{cannot implicitly initialize 
'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' 
(aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 
'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires 
the pointee type be complete when initializing; consider providing a complete 
definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  };
+
+  struct CBBufDeclPos implicit_full_init = {
+    0
+    // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' 
that has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct 
IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete 
and the '__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy' or using the '__sized_by' attribute}}
+    // expected-error@+1{{cannot implicitly initialize 
'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' 
(aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 
'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires 
the pointee type be complete when initializing; consider providing a complete 
definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+  };
+  // Variable is not currently marked as invalid so uses of the variable allows
+  // diagnostics to fire.
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 
'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'struct IncompleteTy' or using the 
'__sized_by' attribute}}
+  implicit_full_init.buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has 
type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because 
the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and 
the '__counted_by' attribute requires the pointee type be complete when 
assigning; consider providing a complete definition for 'Incomplete_t' or using 
the '__sized_by' attribute}}
+  implicit_full_init.buf_typedef = 0x0;
+  // expected-error@+1{{cannot use 'implicit_full_init.buf' with type 'struct 
IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' 
attribute requires the pointee type be complete in this context; consider 
providing a complete definition for 'struct IncompleteTy' or using the 
'__sized_by' attribute}}
+  void* tmp3 = implicit_full_init.buf;
+  // expected-error@+1{{cannot use 'implicit_full_init.buf_typedef' with type 
'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'Incomplete_t' or using the 
'__sized_by' attribute}}
+  void* tmp4 = implicit_full_init.buf_typedef;
+  
+  struct CBBufDeclPos explicit_non_desig_init = {
+    0,
+    // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' that has type 
'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy' or using the '__sized_by' attribute}}
+    0x0,
+    // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' that 
has type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete when initializing; consider providing a complete definition for 
'Incomplete_t' or using the '__sized_by' attribute}}
+    0x0
+  };
+
+  // 
===========================================================================
+  // ## Assignment to fields
+  // 
===========================================================================
+  struct CBBufDeclPos uninit;
+  uninit.count = 0;
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 
'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'struct IncompleteTy' or using the 
'__sized_by' attribute}}
+  uninit.buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has 
type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because 
the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and 
the '__counted_by' attribute requires the pointee type be complete when 
assigning; consider providing a complete definition for 'Incomplete_t' or using 
the '__sized_by' attribute}}
+  uninit.buf_typedef = 0x0;
+  ptr->count = 0;
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 
'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'struct IncompleteTy' or using the 
'__sized_by' attribute}}
+  ptr->buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has 
type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because 
the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and 
the '__counted_by' attribute requires the pointee type be complete when 
assigning; consider providing a complete definition for 'Incomplete_t' or using 
the '__sized_by' attribute}}
+  ptr->buf_typedef = 0x0;
+
+
+  // 
===========================================================================
+  // ## Make sure modifying the fields through other assignment operators is 
not
+  //    allowed
+  // 
===========================================================================
+  uninit.buf++; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
+  ++uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
+  uninit.buf += 1; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
+  uninit.buf_typedef++; // // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ++uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  
+  uninit.buf--; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
+  --uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
+  uninit.buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
+  uninit.buf_typedef--; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  --uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+
+  ptr->buf++; // expected-error{{arithmetic on a pointer to an incomplete type 
'struct IncompleteTy'}}
+  ++ptr->buf; // expected-error{{arithmetic on a pointer to an incomplete type 
'struct IncompleteTy'}}
+  ptr->buf += 1; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
+  ptr->buf--; // expected-error{{arithmetic on a pointer to an incomplete type 
'struct IncompleteTy'}}
+  --ptr->buf; // expected-error{{arithmetic on a pointer to an incomplete type 
'struct IncompleteTy'}}
+  ptr->buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
+
+  ptr->buf_typedef++; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ++ptr->buf_typedef; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ptr->buf_typedef += 1; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ptr->buf_typedef--; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  --ptr->buf_typedef; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+  ptr->buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
+
+  // 
===========================================================================
+  // ## Use of fields in expressions
+  // 
===========================================================================
+  // expected-error@+2{{cannot use 'uninit.buf' with type 'struct IncompleteTy 
* __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  void* addr = 
+    ((char*) uninit.buf ) + 1;
+  // expected-error@+2{{cannot use 'uninit.buf_typedef' with type 
'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'Incomplete_t' or using the 
'__sized_by' attribute}}
+  void* addr_typedef = 
+    ((char*) uninit.buf_typedef ) + 1;
+  // expected-error@+2{{cannot use 'ptr->buf' with type 'struct IncompleteTy * 
__counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  void* addr_ptr = 
+    ((char*) ptr->buf ) + 1;
+  // expected-error@+2{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t 
* __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' 
attribute requires the pointee type be complete in this context; consider 
providing a complete definition for 'Incomplete_t' or using the '__sized_by' 
attribute}}
+  void* addr_ptr_typedef = 
+    ((char*) ptr->buf_typedef ) + 1;
+
+  // 
===========================================================================
+  // ## Take address of fields
+  // 
===========================================================================
+  // TODO: This should be forbidden, not because of the incomplete pointee type
+  // but because in the -fbounds-safety language model the address of a
+  // `counted_by` pointer cannot be taken to avoid it being possible to modify
+  // the `counted_by` pointer through another pointer. Whether or not this
+  // should be forbidden when `-fbounds-safety` is off is TBD.
+  //
+  // The incomplete pointee type isn't actually a problem here for
+  // `-fbounds-safety` because taking the address of a pointer returns a 
pointer
+  // that have the bounds of a single `void*`, so bounds checks on the 
resulting
+  // pointer don't need to know `sizeof(struct IncompleteTy)` but instead
+  // `sizeof(struct IncompleteTy* buf __counted_by(count))` which is just the
+  // size of a pointer.
+  void* take_addr = &uninit.buf;
+  void* take_addr_typedef = &uninit.buf_typedef;
+  void* take_addr_ptr = &ptr->buf;
+  void* take_addr_ptr_typedef = &ptr->buf_typedef;
+
+  // expected-error@+1{{cannot use 'uninit.buf' with type 'struct IncompleteTy 
* __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_zero = &uninit.buf[0];
+  // expected-error@+1{{cannot use 'uninit.buf' with type 'struct IncompleteTy 
* __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_idx = &uninit.buf[idx()];
+
+  // expected-error@+1{{cannot use 'uninit.buf_typedef' with type 
'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'Incomplete_t' or using the 
'__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_zero_typedef = &uninit.buf_typedef[0];
+  // expected-error@+1{{cannot use 'uninit.buf_typedef' with type 
'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'Incomplete_t' or using the 
'__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_idx_typedef = &uninit.buf_typedef[idx()];
+
+  // expected-error@+1{{cannot use 'ptr->buf' with type 'struct IncompleteTy * 
__counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0];
+  // expected-error@+1{{cannot use 'ptr->buf' with type 'struct IncompleteTy * 
__counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()];
+  // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t 
* __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' 
attribute requires the pointee type be complete in this context; consider 
providing a complete definition for 'Incomplete_t' or using the '__sized_by' 
attribute}}
+  struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0];
+  // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t 
* __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' 
attribute requires the pointee type be complete in this context; consider 
providing a complete definition for 'Incomplete_t' or using the '__sized_by' 
attribute}}
+  struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()];
+
+  // 
===========================================================================
+  // ## Use fields as call arguments
+  // 
===========================================================================
+  // expected-error@+1{{cannot use 'uninit.buf' with type 'struct IncompleteTy 
* __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteTy(uninit.buf);
+  // expected-error@+1{{cannot use 'uninit.buf_typedef' with type 
'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'Incomplete_t' or using the 
'__sized_by' attribute}}
+  consume_struct_IncompleteTy(uninit.buf_typedef);
+  // expected-error@+1{{cannot use 'ptr->buf' with type 'struct IncompleteTy * 
__counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteTy(ptr->buf);
+  // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 'Incomplete_t 
* __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the '__counted_by' 
attribute requires the pointee type be complete in this context; consider 
providing a complete definition for 'Incomplete_t' or using the '__sized_by' 
attribute}}
+  consume_struct_IncompleteTy(ptr->buf_typedef);
+
+  // 
===========================================================================
+  // ## Use [] operator on fields
+  // 
===========================================================================
+  // expected-error@+1 2{{cannot use 'uninit.buf' with type 'struct 
IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'struct IncompleteTy' is incomplete and the '__counted_by' 
attribute requires the pointee type be complete in this context; consider 
providing a complete definition for 'struct IncompleteTy' or using the 
'__sized_by' attribute}}
+  uninit.buf[0] = uninit.buf[1];
+  // expected-error@+1 2{{cannot use 'uninit.buf_typedef' with type 
'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'Incomplete_t' or using the 
'__sized_by' attribute}}
+  uninit.buf_typedef[0] = uninit.buf_typedef[1];
+  // expected-error@+1 2{{cannot use 'ptr->buf' with type 'struct IncompleteTy 
* __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 
'struct IncompleteTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy' or using the '__sized_by' attribute}}
+  ptr->buf[0] = ptr->buf[1];
+  // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with type 
'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the 
pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'Incomplete_t' or using the 
'__sized_by' attribute}}
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+// 
=============================================================================
+// ## Global initialization
+// 
=============================================================================
+
+struct CBBufDeclPos global_explicit_desig_init = {
+  .count = 0,
+  // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' that has type 
'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy' or using the '__sized_by' attribute}}
+  .buf = 0x0,
+  // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has 
type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because 
the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and 
the '__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'Incomplete_t' or 
using the '__sized_by' attribute}}
+  .buf_typedef = 0x0
+};
+
+void use_global_explicit_desig_init(void) {
+  // Variable isn't marked as invalid so diagnostics still fire
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' that has type 
'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'struct IncompleteTy' or using the 
'__sized_by' attribute}}
+  global_explicit_desig_init.buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' that has 
type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because 
the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and 
the '__counted_by' attribute requires the pointee type be complete when 
assigning; consider providing a complete definition for 'Incomplete_t' or using 
the '__sized_by' attribute}}
+  global_explicit_desig_init.buf_typedef = 0x0;
+}
+
+struct CBBufDeclPos global_partial_explicit_desig_init = {
+  .count = 0,
+  // .buf and .buf_typedef are implicit zero initialized
+  // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' that 
has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy 
*') because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy' or using the '__sized_by' attribute}}
+  // expected-error@+1{{cannot implicitly initialize 
'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' 
(aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 
'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires 
the pointee type be complete when initializing; consider providing a complete 
definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+};
+
+struct CBBufDeclPos global_implicit_full_init = {
+  0
+  // expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' that 
has type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy 
*') because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy' or using the '__sized_by' attribute}}
+  // expected-error@+1{{cannot implicitly initialize 
'CBBufDeclPos::buf_typedef' that has type 'Incomplete_t * __counted_by(count)' 
(aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 
'struct IncompleteTy') is incomplete and the '__counted_by' attribute requires 
the pointee type be complete when initializing; consider providing a complete 
definition for 'Incomplete_t' or using the '__sized_by' attribute}}
+};
+
+struct CBBufDeclPos global_explicit_non_desig_init = {
+  0,
+  // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' that has type 
'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy' or using the '__sized_by' attribute}}
+  0x0,
+  // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' that has 
type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because 
the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete and 
the '__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'Incomplete_t' or 
using the '__sized_by' attribute}}
+  0x0
+};
+
+extern struct CBBufDeclPos global_declaration; // OK
+
+// TODO: These tentative definitions are implicitly empty initialized to zero.
+// This should generate an error diagnostic but currently doesn't. There should
+// be a carve out to allow `__counted_by(0)` which is the only constant count
+// version of the attribute where it is valid to assign NULL.
+struct CBBufDeclPos global_tentative_defn;
+static struct CBBufDeclPos global_tentative_defn2;
+
+// 
=============================================================================
+// ## Completing the definition of the type allows use of CBBufDeclPos fields
+// 
=============================================================================
+struct IncompleteTy {
+  int field;
+};
+
+void test_CBBufDeclPos_completed(struct CBBufDeclPos* ptr) {
+  // Initialization is ok
+  struct CBBufDeclPos explicit_desig_init = {
+    .count = 0,
+    .buf = 0x0,
+    .buf_typedef = 0x0
+  };
+
+  struct CBBufDeclPos partial_explicit_desig_init = {
+    .count = 0,
+    // .buf and .buf_typedef are implicit zero initialized
+  };
+
+  struct CBBufDeclPos implicit_full_init = {0};
+  
+  struct CBBufDeclPos explicit_non_desig_init = {
+    0,
+    0x0,
+    0x0
+  };
+
+  // Assignment to fields is ok
+  ptr->buf = 0x0;
+  ptr->buf_typedef = 0x0;
+
+  // Use of fields in expressions is ok
+  void* tmp = ptr->buf;
+  void* tmp2 = ptr->buf_typedef;
+
+  // Take address of fields is ok
+  void* take_addr_ptr = &ptr->buf;
+  void* take_addr_ptr_typedef = &ptr->buf_typedef;
+
+  struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0];
+  struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()];
+  struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0];
+  struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()];
+
+  // As call arguments is ok
+  consume_struct_IncompleteTy(ptr->buf);
+  consume_struct_IncompleteTy(ptr->buf_typedef);
+
+  // In [] operator is ok
+  ptr->buf[0] = ptr->buf[1];
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+// Global initialization is ok
+
+struct CBBufDeclPos global_explicit_desig_init_completed = {
+  .count = 0,
+  .buf = 0x0,
+  .buf_typedef = 0x0
+};
+
+struct CBBufDeclPos global_partial_explicit_desig_init_completed = {
+  .count = 0,
+  // .buf and .buf_typedef are implicit zero initialized
+};
+
+struct CBBufDeclPos global_implicit_full_init_completed = {0};
+
+struct CBBufDeclPos global_explicit_non_desig_init_completed = {
+  0,
+  0x0,
+  0x0
+};
+
+extern struct CBBufDeclPos global_declaration;
+struct CBBufDeclPos global_tentative_defn;
+static struct CBBufDeclPos global_tentative_defn2;
+
+// 
=============================================================================
+// # Struct incomplete type with attribute in the pointer position
+// 
=============================================================================
+
+// expected-note@+1 8{{forward declaration of 'Incomplete_ty2' (aka 'struct 
IncompleteTy2')}}
+struct IncompleteTy2; // expected-note 8{{forward declaration of 'struct 
IncompleteTy2'}}
+typedef struct IncompleteTy2 Incomplete_ty2;
+
+void consume_struct_IncompleteTy2(struct IncompleteTy2* buf);
+
+struct CBBufTyPos {
+  int count;
+  struct IncompleteTy2* __counted_by(count) buf ; // OK expected-note 
8{{__counted_by attribute is here}}
+  Incomplete_ty2 *__counted_by(count) buf_typedef; // OK expected-note 
8{{__counted_by attribute is here}}
+};
+
+void use_CBBufTyPos(struct CBBufTyPos* ptr) {
+  struct CBBufTyPos explicit_desig_init = {
+    .count = 0,
+    // expected-error@+1{{cannot initialize 'CBBufTyPos::buf' that has type 
'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') 
because the pointee type 'struct IncompleteTy2' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy2' or using the '__sized_by' attribute}}
+    .buf = 0x0,
+    // expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' that has 
type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') 
because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete when initializing; consider providing a complete definition for 
'Incomplete_ty2' or using the '__sized_by' attribute}}
+    .buf_typedef = 0x0
+  };
+
+  // Assignment
+  // expected-error@+1{{cannot assign to 'CBBufTyPos::buf' that has type 
'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') 
because the pointee type 'struct IncompleteTy2' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'struct IncompleteTy2' or using 
the '__sized_by' attribute}}
+  explicit_desig_init.buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' that has 
type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') 
because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete when assigning; consider providing a complete definition for 
'Incomplete_ty2' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf_typedef = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufTyPos::buf' that has type 
'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') 
because the pointee type 'struct IncompleteTy2' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'struct IncompleteTy2' or using 
the '__sized_by' attribute}}
+  ptr->buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' that has 
type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') 
because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete when assigning; consider providing a complete definition for 
'Incomplete_ty2' or using the '__sized_by' attribute}}
+  ptr->buf_typedef = 0x0;
+
+  // Use
+  // expected-error@+2{{cannot use 'ptr->buf' with type 'struct IncompleteTy2 
* __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 
'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}}
+  void* addr = 
+    ((char*) ptr->buf ) + 1;
+  // expected-error@+2{{cannot use 'ptr->buf_typedef' with type 
'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because 
the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete 
and the '__counted_by' attribute requires the pointee type be complete in this 
context; consider providing a complete definition for 'Incomplete_ty2' or using 
the '__sized_by' attribute}}
+  void* addr_typedef = 
+    ((char*) ptr->buf_typedef ) + 1;
+
+  // expected-error@+1{{cannot use 'ptr->buf' with type 'struct IncompleteTy2 
* __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 
'struct IncompleteTy2' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'struct IncompleteTy2' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteTy2(ptr->buf);
+  // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 
'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because 
the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete 
and the '__counted_by' attribute requires the pointee type be complete in this 
context; consider providing a complete definition for 'Incomplete_ty2' or using 
the '__sized_by' attribute}}
+  consume_struct_IncompleteTy2(ptr->buf_typedef);
+
+  // expected-error@+1 2{{cannot use 'ptr->buf' with type 'struct 
IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the 
pointee type 'struct IncompleteTy2' is incomplete and the '__counted_by' 
attribute requires the pointee type be complete in this context; consider 
providing a complete definition for 'struct IncompleteTy2' or using the 
'__sized_by' attribute}}
+  ptr->buf[0] = ptr->buf[1];
+  // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with type 
'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because 
the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete 
and the '__counted_by' attribute requires the pointee type be complete in this 
context; consider providing a complete definition for 'Incomplete_ty2' or using 
the '__sized_by' attribute}}
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+struct CBBufTyPos global_explicit_desig_init_struct_type_pos = {
+  .count = 0,
+  // expected-error@+1{{cannot initialize 'CBBufTyPos::buf' that has type 
'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') 
because the pointee type 'struct IncompleteTy2' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'struct 
IncompleteTy2' or using the '__sized_by' attribute}}
+  .buf = 0x0,
+  // expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' that has 
type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') 
because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete when initializing; consider providing a complete definition for 
'Incomplete_ty2' or using the '__sized_by' attribute}}
+  .buf_typedef = 0x0
+};
+
+// Defining the type makes `CBBufTyPos` fields usable
+struct IncompleteTy2 {
+  int field;
+};
+
+void use_CBBufTyPos_completed(struct CBBufTyPos* ptr) {
+  ptr->buf = 0x0;
+  ptr->buf_typedef = 0x0;
+  void* addr = ((char*) ptr->buf) + 1;
+  void* addr_typedef = ((char*) ptr->buf_typedef) + 1;
+}
+
+// 
=============================================================================
+// # union incomplete type
+// 
=============================================================================
+
+// expected-note@+1 8{{forward declaration of 'IncompleteUnion_ty' (aka 'union 
IncompleteUnionTy')}}
+union IncompleteUnionTy; // expected-note 8{{forward declaration of 'union 
IncompleteUnionTy'}}
+typedef union IncompleteUnionTy IncompleteUnion_ty;
+
+void consume_struct_IncompleteUnionTy(union IncompleteUnionTy* buf);
+
+struct CBBufUnionTyPos {
+  int count;
+  union IncompleteUnionTy* __counted_by(count) buf ; // OK expected-note 
8{{__counted_by attribute is here}}
+  IncompleteUnion_ty *__counted_by(count) buf_typedef; // OK expected-note 
8{{__counted_by attribute is here}}
+};
+
+void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) {
+  struct CBBufUnionTyPos explicit_desig_init = {
+    .count = 0,
+    // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf' that has 
type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union 
IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete when initializing; consider providing a complete definition for 'union 
IncompleteUnionTy' or using the '__sized_by' attribute}}
+    .buf = 0x0,
+    // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' 
that has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union 
IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union 
IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the 
pointee type be complete when initializing; consider providing a complete 
definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+    .buf_typedef = 0x0
+  };
+
+  // Assignment
+  // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' that has type 
'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy 
*') because the pointee type 'union IncompleteUnionTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'union IncompleteUnionTy' or using 
the '__sized_by' attribute}}
+  explicit_desig_init.buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' that 
has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union 
IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union 
IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the 
pointee type be complete when assigning; consider providing a complete 
definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf_typedef = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' that has type 
'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy 
*') because the pointee type 'union IncompleteUnionTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'union IncompleteUnionTy' or using 
the '__sized_by' attribute}}
+  ptr->buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' that 
has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union 
IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union 
IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the 
pointee type be complete when assigning; consider providing a complete 
definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  ptr->buf_typedef = 0x0;
+
+  // Use
+  // expected-error@+2{{cannot use 'ptr->buf' with type 'union 
IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') 
because the pointee type 'union IncompleteUnionTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'union IncompleteUnionTy' or using 
the '__sized_by' attribute}}
+  void* addr = 
+    ((char*) ptr->buf ) + 1;
+  // expected-error@+2{{cannot use 'ptr->buf_typedef' with type 
'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') 
because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') 
is incomplete and the '__counted_by' attribute requires the pointee type be 
complete in this context; consider providing a complete definition for 
'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  void* addr_typedef = 
+    ((char*) ptr->buf_typedef ) + 1;
+
+  // expected-error@+1{{cannot use 'ptr->buf' with type 'union 
IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') 
because the pointee type 'union IncompleteUnionTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'union IncompleteUnionTy' or using 
the '__sized_by' attribute}}
+  consume_struct_IncompleteUnionTy(ptr->buf);
+  // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 
'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') 
because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') 
is incomplete and the '__counted_by' attribute requires the pointee type be 
complete in this context; consider providing a complete definition for 
'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteUnionTy(ptr->buf_typedef);
+
+  // expected-error@+1 2{{cannot use 'ptr->buf' with type 'union 
IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') 
because the pointee type 'union IncompleteUnionTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete in this context; 
consider providing a complete definition for 'union IncompleteUnionTy' or using 
the '__sized_by' attribute}}
+  ptr->buf[0] = ptr->buf[1];
+  // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with type 
'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') 
because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') 
is incomplete and the '__counted_by' attribute requires the pointee type be 
complete in this context; consider providing a complete definition for 
'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+struct CBBufUnionTyPos global_explicit_desig_init_union_type_pos = {
+  .count = 0,
+  // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf' that has type 
'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy 
*') because the pointee type 'union IncompleteUnionTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'union 
IncompleteUnionTy' or using the '__sized_by' attribute}}
+  .buf = 0x0,
+  // expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' that 
has type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union 
IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union 
IncompleteUnionTy') is incomplete and the '__counted_by' attribute requires the 
pointee type be complete when initializing; consider providing a complete 
definition for 'IncompleteUnion_ty' or using the '__sized_by' attribute}}
+  .buf_typedef = 0x0
+};
+
+// Defining the type makes `CBBufUnionTyPos` fields usable
+union IncompleteUnionTy {
+  int field;
+};
+
+void use_CBBufUnionTyPos_completed(struct CBBufUnionTyPos* ptr) {
+  ptr->buf = 0x0;
+  ptr->buf_typedef = 0x0;
+  void* addr = ((char*) ptr->buf) + 1;
+  void* addr_typedef = ((char*) ptr->buf_typedef) + 1;
+}
+
+// 
=============================================================================
+// # enum incomplete type
+// 
=============================================================================
+
+// expected-note@+1 8{{forward declaration of 'IncompleteEnum_ty' (aka 'enum 
IncompleteEnumTy')}}
+enum IncompleteEnumTy; // expected-note 8{{forward declaration of 'enum 
IncompleteEnumTy'}}
+typedef enum IncompleteEnumTy IncompleteEnum_ty;
+
+void consume_struct_IncompleteEnumTy(enum IncompleteEnumTy* buf);
+
+struct CBBufEnumTyPos {
+  int count;
+  enum IncompleteEnumTy* __counted_by(count) buf ; // OK expected-note 
8{{__counted_by attribute is here}}
+  IncompleteEnum_ty *__counted_by(count) buf_typedef; // OK expected-note 
8{{__counted_by attribute is here}}
+};
+
+void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) {
+  struct CBBufEnumTyPos explicit_desig_init = {
+    .count = 0,
+    // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf' that has 
type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy 
*') because the pointee type 'enum IncompleteEnumTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'enum 
IncompleteEnumTy' or using the '__sized_by' attribute}}
+    .buf = 0x0,
+    // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' that 
has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy 
*') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') 
is incomplete and the '__counted_by' attribute requires the pointee type be 
complete when initializing; consider providing a complete definition for 
'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+    .buf_typedef = 0x0
+  };
+
+  // Assignment
+  // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' that has type 
'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') 
because the pointee type 'enum IncompleteEnumTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'enum IncompleteEnumTy' or using 
the '__sized_by' attribute}}
+  explicit_desig_init.buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' that 
has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy 
*') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') 
is incomplete and the '__counted_by' attribute requires the pointee type be 
complete when assigning; consider providing a complete definition for 
'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  explicit_desig_init.buf_typedef = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' that has type 
'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') 
because the pointee type 'enum IncompleteEnumTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when assigning; 
consider providing a complete definition for 'enum IncompleteEnumTy' or using 
the '__sized_by' attribute}}
+  ptr->buf = 0x0;
+  // expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' that 
has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy 
*') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') 
is incomplete and the '__counted_by' attribute requires the pointee type be 
complete when assigning; consider providing a complete definition for 
'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  ptr->buf_typedef = 0x0;
+
+  // Use
+  // expected-error@+2{{cannot use 'ptr->buf' with type 'enum IncompleteEnumTy 
* __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 
'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}}
+  void* addr = 
+    ((char*) ptr->buf ) + 1;
+  // expected-error@+2{{cannot use 'ptr->buf_typedef' with type 
'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') 
because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete in this context; consider providing a complete definition for 
'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  void* addr_typedef = 
+    ((char*) ptr->buf_typedef ) + 1;
+
+  // expected-error@+1{{cannot use 'ptr->buf' with type 'enum IncompleteEnumTy 
* __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 
'enum IncompleteEnumTy' is incomplete and the '__counted_by' attribute requires 
the pointee type be complete in this context; consider providing a complete 
definition for 'enum IncompleteEnumTy' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteEnumTy(ptr->buf);
+  // expected-error@+1{{cannot use 'ptr->buf_typedef' with type 
'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') 
because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete in this context; consider providing a complete definition for 
'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  consume_struct_IncompleteEnumTy(ptr->buf_typedef);
+
+  // expected-error@+1 2{{cannot use 'ptr->buf' with type 'enum 
IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because 
the pointee type 'enum IncompleteEnumTy' is incomplete and the '__counted_by' 
attribute requires the pointee type be complete in this context; consider 
providing a complete definition for 'enum IncompleteEnumTy' or using the 
'__sized_by' attribute}}
+  ptr->buf[0] = ptr->buf[1];
+  // expected-error@+1 2{{cannot use 'ptr->buf_typedef' with type 
'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') 
because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is 
incomplete and the '__counted_by' attribute requires the pointee type be 
complete in this context; consider providing a complete definition for 
'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  ptr->buf_typedef[0] = ptr->buf_typedef[1];
+}
+
+struct CBBufEnumTyPos global_explicit_desig_init_enum_type_pos = {
+  .count = 0,
+  // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf' that has type 
'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') 
because the pointee type 'enum IncompleteEnumTy' is incomplete and the 
'__counted_by' attribute requires the pointee type be complete when 
initializing; consider providing a complete definition for 'enum 
IncompleteEnumTy' or using the '__sized_by' attribute}}
+  .buf = 0x0,
+  // expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' that 
has type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy 
*') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') 
is incomplete and the '__counted_by' attribute requires the pointee type be 
complete when initializing; consider providing a complete definition for 
'IncompleteEnum_ty' or using the '__sized_by' attribute}}
+  .buf_typedef = 0x0
+};
+
+// Defining the type makes `CBBufEnumTyPos` fields usable
+enum IncompleteEnumTy {
+  ONE,
+  TWO
+};
+
+void use_CBBufEnumTyPos_completed(struct CBBufEnumTyPos* ptr) {
+  ptr->buf = 0x0;
+  ptr->buf_typedef = 0x0;
+  void* addr = ((char*) ptr->buf) + 1;
+  void* addr_typedef = ((char*) ptr->buf_typedef) + 1;
+}
+
+// Make a complete enum by providing an underlying type
+enum CompleteEnumTy : unsigned;
+typedef enum CompleteEnumTy CompleteEnum_ty;
+struct CBBufEnumTyPos2 {
+  int count;
+  enum CompleteEnumTy* __counted_by(count) buf;
+  CompleteEnum_ty *__counted_by(count) buf_typedef;
+};
+
+void use_CBBufEnumTyPos2(struct CBBufEnumTyPos2* ptr) {
+  struct CBBufEnumTyPos2 explicit_desig_init = {
+    .count = 0,
+    .buf = 0x0, // OK
+    .buf_typedef = 0x0 // OK
+  };
+}
+
+// Make a complete enum by providing a concrete declaration
+enum CompleteEnumTy2 {
+  VALUE_ONE,
+  VALUE_TWO
+};
+typedef enum CompleteEnumTy2 CompleteEnum_ty2;
+struct CBBufEnumTyPos3 {
+  int count;
+  enum CompleteEnumTy2* __counted_by(count) buf;
+  CompleteEnum_ty2 *__counted_by(count) buf_typedef;
+};
+
+void use_CBBufEnumTyPos3(struct CBBufEnumTyPos3* ptr) {
+  struct CBBufEnumTyPos3 explicit_desig_init = {
+    .count = 0,
+    .buf = 0x0, // OK
+    .buf_typedef = 0x0 // OK
+  };
+}
+
+
+// 
=============================================================================
+// # Array of __counted_by pointers
+// 
=============================================================================
+
+struct IncompleteTy3;
+
+struct CBBufFAMofCountedByPtrs {
+  int size;
+  // TODO: This is misleading. The attribute is written in the type position
+  // but clang currently doesn't treat it like that and it gets treated as
+  // an attribute on the array, rather than on the element type.
+  struct IncompleteTy3* __counted_by(size) arr[];
+};
+
+void arr_of_counted_by_ptr(struct CBBufFAMofCountedByPtrs* ptr) {
+  // TODO: Should be disallowed once parsing attributes in the type position
+  // works.
+  ptr->arr[0] = 0x0;
+  void* addr = ((char*) ptr->arr[0]) + 1;
+}
diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs.c 
b/clang/test/Sema/attr-counted-by-struct-ptrs.c
index 321d6aafbeba2..c05d18262e2b7 100644
--- a/clang/test/Sema/attr-counted-by-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-struct-ptrs.c
@@ -21,14 +21,12 @@ struct on_member_pointer_complete_ty {
 
 struct on_member_pointer_incomplete_ty {
   int count;
-  // expected-error@+1{{'counted_by' 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(count);
+  struct size_unknown * buf __counted_by(count); // ok
 };
 
 struct on_member_pointer_const_incomplete_ty {
   int count;
-  // expected-error@+1{{'counted_by' 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(count);
+  const struct size_unknown * buf __counted_by(count); // ok
 };
 
 struct on_member_pointer_void_ty {
@@ -115,14 +113,12 @@ struct on_member_pointer_complete_ty_ty_pos {
 
 struct on_member_pointer_incomplete_ty_ty_pos {
   int count;
-  // expected-error@+1{{'counted_by' cannot be applied to a pointer with 
pointee of unknown size because 'struct size_unknown' is an incomplete type}}
-  struct size_unknown * __counted_by(count) buf;
+  struct size_unknown * __counted_by(count) buf; // ok
 };
 
 struct on_member_pointer_const_incomplete_ty_ty_pos {
   int count;
-  // expected-error@+1{{'counted_by' 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(count) buf;
+  const struct size_unknown * __counted_by(count) buf; // ok
 };
 
 struct on_member_pointer_void_ty_ty_pos {
diff --git a/clang/test/Sema/attr-counted-by-vla.c 
b/clang/test/Sema/attr-counted-by-vla.c
index 35737c03f3222..5b1b4833dce7d 100644
--- a/clang/test/Sema/attr-counted-by-vla.c
+++ b/clang/test/Sema/attr-counted-by-vla.c
@@ -98,8 +98,7 @@ struct array_of_ints_count {
 
 struct not_a_fam {
   int count;
-  // expected-error@+1{{'counted_by' cannot be applied to a pointer with 
pointee of unknown size because 'struct bar' is an incomplete type}}
-  struct bar *non_fam __counted_by(count);
+  struct bar *non_fam __counted_by(count); // ok
 };
 
 struct not_a_c99_fam {

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

Reply via email to