ahatanak updated this revision to Diff 62560.
ahatanak added a comment.

The new patch defines a new attribute "flexible_array", which gets attached to 
the last array member of a struct.

I made changes to clang to treat arrays marked "flexible_array" as C99's 
flexible array members where it made sense to do so. There are several places 
where C99's flexible arrays and "flexible_array" are treated differently. For 
example, the attribute doesn't change the way arguments are passed or values 
are returned from a function. It doesn't change objective-c's type encoding 
either.


http://reviews.llvm.org/D21453

Files:
  include/clang/AST/Decl.h
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/AttributeList.h
  lib/AST/Decl.cpp
  lib/AST/ExprConstant.cpp
  lib/CodeGen/CGExpr.cpp
  lib/CodeGen/CodeGenTBAA.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaDeclCXX.cpp
  lib/Sema/SemaExpr.cpp
  lib/Serialization/ASTReaderDecl.cpp
  lib/Serialization/ASTWriterDecl.cpp
  test/CodeGen/object-size.c
  test/CodeGenCXX/catch-undef-behavior.cpp
  test/SemaCXX/flexible-array-attr.cpp

Index: test/SemaCXX/flexible-array-attr.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/flexible-array-attr.cpp
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+
+int g0[16] __attribute__((flexible_array)); // expected-error {{'flexible_array' attribute only applies to fields}}
+
+struct S0 {
+  int a[4];
+  int foo1() __attribute__((flexible_array)); // expected-error {{'flexible_array' attribute only applies to fields}}
+};
+
+struct S1 {
+  int a[4];
+  int *b __attribute__((flexible_array)); // expected-error {{'flexible_array' attribute only applies to fixed sized array members}}
+};
+
+struct S2 {
+  int a[4];
+  int b[4] __attribute__((flexible_array)); // expected-error {{'flexible_array' attribute only applies to the last member of a struct}}
+  int c[4];
+};
+
+struct S3 {
+  int a[4];
+  int b[] __attribute__((flexible_array)); // expected-error {{'flexible_array' attribute only applies to fixed sized array members}}
+};
+
+struct S4 {
+  int a[4];
+  int b[0] __attribute__((flexible_array)); // expected-error {{'flexible_array' attribute only applies to array members that have at least one element}}
+};
+
+template<int N>
+struct S5 {
+  int a[4];
+  int b[N] __attribute__((flexible_array)); // expected-error {{'flexible_array' attribute only applies to fixed sized array members}}
+};
+
+struct S6 {
+  int a[4];
+  int b[1] __attribute__((flexible_array));
+};
+
+struct S7 : S6 { // expected-error {{base class 'S6' has a flexible array member}}
+};
+
+struct S8 {
+  int a;
+  S6 s6; // expected-error {{struct with a member marked 'flexible_array' cannot be nested}}
+};
+
+union U0 {
+  int a[4];
+  int b[4] __attribute__((flexible_array)); // expected-error {{'flexible_array' attribute only applies to members of structs or classes}}
+};
+
+int lambda_capture(S6 a) { // expected-note {{'a' declared here}}
+  return [a](){ return 0; }(); // expected-error {{variable 'a' with flexible array member cannot be captured in a lambda expression}}
+}
Index: test/CodeGenCXX/catch-undef-behavior.cpp
===================================================================
--- test/CodeGenCXX/catch-undef-behavior.cpp
+++ test/CodeGenCXX/catch-undef-behavior.cpp
@@ -327,6 +327,17 @@
   return incomplete[n];
 }
 
+struct FlexibleArray {
+  int a1[4];
+  int a2[4] __attribute__((flexible_array));
+};
+
+// CHECK-LABEL: @_Z14flexible_array
+int flexible_array(FlexibleArray *p, int n) {
+  // CHECK-NOT: call void @__ubsan_handle_out_of_bounds(
+  return p->a2[n];
+}
+
 typedef __attribute__((ext_vector_type(4))) int V4I;
 // CHECK-LABEL: @_Z12vector_index
 int vector_index(V4I v, int n) {
Index: test/CodeGen/object-size.c
===================================================================
--- test/CodeGen/object-size.c
+++ test/CodeGen/object-size.c
@@ -517,3 +517,19 @@
   // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
   gi = __builtin_object_size(&dsv[9].snd[0], 1);
 }
+
+struct S0 {
+  int a[16];
+  int b[16] __attribute__((flexible_array));
+};
+
+// CHECK-LABEL: @test32
+void test32() {
+  struct S0 *s0;
+
+  // CHECK: store i32 64, i32* @gi
+  gi = __builtin_object_size(s0->a, 1);
+
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(s0->b, 1);
+}
Index: lib/Serialization/ASTWriterDecl.cpp
===================================================================
--- lib/Serialization/ASTWriterDecl.cpp
+++ lib/Serialization/ASTWriterDecl.cpp
@@ -457,6 +457,7 @@
 void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) {
   VisitTagDecl(D);
   Record.push_back(D->hasFlexibleArrayMember());
+  Record.push_back(D->hasFlexibleArrayAttr());
   Record.push_back(D->isAnonymousStructOrUnion());
   Record.push_back(D->hasObjectMember());
   Record.push_back(D->hasVolatileMember());
@@ -1820,6 +1821,7 @@
   Abv->Add(BitCodeAbbrevOp(0));                         // ExtInfoKind
   // RecordDecl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // FlexibleArrayMember
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // FlexibleArrayAttr
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // AnonymousStructUnion
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // hasObjectMember
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // hasVolatileMember
Index: lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- lib/Serialization/ASTReaderDecl.cpp
+++ lib/Serialization/ASTReaderDecl.cpp
@@ -721,6 +721,7 @@
 ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) {
   RedeclarableResult Redecl = VisitTagDecl(RD);
   RD->setHasFlexibleArrayMember(Record[Idx++]);
+  RD->setHasFlexibleArrayAttr(Record[Idx++]);
   RD->setAnonymousStructOrUnion(Record[Idx++]);
   RD->setHasObjectMember(Record[Idx++]);
   RD->setHasVolatileMember(Record[Idx++]);
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -13216,7 +13216,7 @@
   // Prohibit structs with flexible array members too.
   // We cannot capture what is in the tail end of the struct.
   if (const RecordType *VTTy = Var->getType()->getAs<RecordType>()) {
-    if (VTTy->getDecl()->hasFlexibleArrayMember()) {
+    if (VTTy->getDecl()->hasFlexibleArrayMemberOrAttr()) {
       if (Diagnose) {
         if (IsBlock)
           S.Diag(Loc, diag::err_ref_flexarray_type);
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -1443,7 +1443,7 @@
   //     the flexible array member would index into the subsequent base.
   //   - If the layout determines that base comes before the derived class,
   //     the flexible array member would index into the derived class.
-  if (CXXBaseDecl->hasFlexibleArrayMember()) {
+  if (CXXBaseDecl->hasFlexibleArrayMemberOrAttr()) {
     Diag(BaseLoc, diag::err_base_class_has_flexible_array_member)
       << CXXBaseDecl->getDeclName();
     return nullptr;
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -1803,6 +1803,33 @@
       Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex()));
 }
 
+static void handleFlexibleArrayAttr(Sema &S, Decl *D,
+                                    const AttributeList &Attr) {
+  auto *FD = cast<FieldDecl>(D);
+  auto *CAT = dyn_cast<ConstantArrayType>(FD->getType().getTypePtr());
+
+  if (FD->getParent()->isUnion()) {
+    S.Diag(Attr.getLoc(), diag::err_flexible_array_attribute)
+        << "members of structs or classes";
+    return;
+  }
+
+  if (!CAT) {
+    S.Diag(Attr.getLoc(), diag::err_flexible_array_attribute)
+        << "fixed sized array members";
+    return;
+  }
+
+  if (CAT->getSize().ult(1)) {
+    S.Diag(Attr.getLoc(), diag::err_flexible_array_attribute)
+        << "array members that have at least one element";
+    return;
+  }
+
+  D->addAttr(::new (S.Context) FlexibleArrayAttr(
+      Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex()));
+}
+
 static void handleDisableTailCallsAttr(Sema &S, Decl *D,
                                        const AttributeList &Attr) {
   if (checkAttrMutualExclusion<NakedAttr>(S, D, Attr.getRange(),
@@ -5669,6 +5696,9 @@
   case AttributeList::AT_NotTailCalled:
     handleNotTailCalledAttr(S, D, Attr);
     break;
+  case AttributeList::AT_FlexibleArray:
+    handleFlexibleArrayAttr(S, D, Attr);
+    break;
   case AttributeList::AT_DisableTailCalls:
     handleDisableTailCallsAttr(S, D, Attr);
     break;
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -13919,6 +13919,17 @@
       continue;
     }
 
+    // Set HasFlexibleArrayAttr. Only the last member of a struct can be marked
+    // flexible_array.
+    if (FD->hasAttr<FlexibleArrayAttr>()) {
+      if (i + 1 == end)
+        Record->setHasFlexibleArrayAttr(true);
+      else
+        Diag(FD->getLocation(), diag::err_flexible_array_attribute)
+            << "the last member of a struct";
+    }
+
+
     // C99 6.7.2.1p2:
     //   A structure or union shall not contain a member with
     //   incomplete or function type (hence, a structure shall not
@@ -14000,24 +14011,27 @@
       EnclosingDecl->setInvalidDecl();
       continue;
     } else if (const RecordType *FDTTy = FDTy->getAs<RecordType>()) {
-      if (Record && FDTTy->getDecl()->hasFlexibleArrayMember()) {
-        // A type which contains a flexible array member is considered to be a
-        // flexible array member.
-        Record->setHasFlexibleArrayMember(true);
-        if (!Record->isUnion()) {
-          // If this is a struct/class and this is not the last element, reject
-          // it.  Note that GCC supports variable sized arrays in the middle of
-          // structures.
-          if (i + 1 != Fields.end())
-            Diag(FD->getLocation(), diag::ext_variable_sized_type_in_struct)
-              << FD->getDeclName() << FD->getType();
-          else {
-            // We support flexible arrays at the end of structs in
-            // other structs as an extension.
-            Diag(FD->getLocation(), diag::ext_flexible_array_in_struct)
-              << FD->getDeclName();
+      if (Record) {
+        if (FDTTy->getDecl()->hasFlexibleArrayMember()) {
+          // A type which contains a flexible array member is considered to be a
+          // flexible array member.
+          Record->setHasFlexibleArrayMember(true);
+          if (!Record->isUnion()) {
+            // If this is a struct/class and this is not the last element, reject
+            // it.  Note that GCC supports variable sized arrays in the middle of
+            // structures.
+            if (i + 1 != Fields.end())
+              Diag(FD->getLocation(), diag::ext_variable_sized_type_in_struct)
+                  << FD->getDeclName() << FD->getType();
+            else {
+              // We support flexible arrays at the end of structs in
+              // other structs as an extension.
+              Diag(FD->getLocation(), diag::ext_flexible_array_in_struct)
+                  << FD->getDeclName();
+            }
           }
-        }
+        } else if (FDTTy->getDecl()->hasFlexibleArrayAttr())
+          Diag(FD->getLocation(), diag::err_flexible_array_nested);
       }
       if (isa<ObjCContainerDecl>(EnclosingDecl) &&
           RequireNonAbstractType(FD->getLocation(), FD->getType(),
Index: lib/CodeGen/CodeGenTBAA.cpp
===================================================================
--- lib/CodeGen/CodeGenTBAA.cpp
+++ lib/CodeGen/CodeGenTBAA.cpp
@@ -180,7 +180,7 @@
 
   if (const RecordType *TTy = QTy->getAs<RecordType>()) {
     const RecordDecl *RD = TTy->getDecl()->getDefinition();
-    if (RD->hasFlexibleArrayMember())
+    if (RD->hasFlexibleArrayMemberOrAttr())
       return false;
 
     // TODO: Handle C++ base classes.
@@ -231,7 +231,7 @@
 static bool isTBAAPathStruct(QualType QTy) {
   if (const RecordType *TTy = QTy->getAs<RecordType>()) {
     const RecordDecl *RD = TTy->getDecl()->getDefinition();
-    if (RD->hasFlexibleArrayMember())
+    if (RD->hasFlexibleArrayMemberOrAttr())
       return false;
     // RD can be struct, union, class, interface or enum.
     // For now, we only handle struct and class.
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -670,27 +670,23 @@
 /// Determine whether this expression refers to a flexible array member in a
 /// struct. We disable array bounds checks for such members.
 static bool isFlexibleArrayMemberExpr(const Expr *E) {
-  // For compatibility with existing code, we treat arrays of length 0 or
-  // 1 as flexible array members.
-  const ArrayType *AT = E->getType()->castAsArrayTypeUnsafe();
-  if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
-    if (CAT->getSize().ugt(1))
-      return false;
-  } else if (!isa<IncompleteArrayType>(AT))
-    return false;
-
-  E = E->IgnoreParens();
-
-  // A flexible array member must be the last member in the class.
-  if (const auto *ME = dyn_cast<MemberExpr>(E)) {
+  if (const auto *ME = dyn_cast<MemberExpr>(E->IgnoreParens()))
     // FIXME: If the base type of the member expr is not FD->getParent(),
     // this should not be treated as a flexible array member access.
     if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) {
-      RecordDecl::field_iterator FI(
-          DeclContext::decl_iterator(const_cast<FieldDecl *>(FD)));
-      return ++FI == FD->getParent()->field_end();
+      if (FD->getParent()->hasFlexibleArrayMemberOrAttr())
+        return true;
+      // For compatibility with existing code, we treat arrays of length 0 or
+      // 1 that are the last member in a class as flexible array members.
+      const ArrayType *AT = E->getType()->castAsArrayTypeUnsafe();
+      if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
+        RecordDecl::field_iterator FI(
+            DeclContext::decl_iterator(const_cast<FieldDecl *>(FD)));
+        if (std::next(FI) == FD->getParent()->field_end() &&
+            CAT->getSize().ule(1))
+          return true;
+      }
     }
-  }
 
   return false;
 }
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -166,9 +166,12 @@
     /// Indicator of whether the most-derived object is an array element.
     bool MostDerivedIsArrayElement : 1;
 
+    /// Indicator of whether the last array added is marked flexible_array.
+    bool IsFlexibleArray : 1;
+
     /// The length of the path to the most-derived object of which this is a
     /// subobject.
-    unsigned MostDerivedPathLength : 29;
+    unsigned MostDerivedPathLength : 28;
 
     /// The size of the array of which the most-derived object is an element.
     /// This will always be 0 if the most-derived object is not an array
@@ -188,13 +191,14 @@
 
     explicit SubobjectDesignator(QualType T)
         : Invalid(false), IsOnePastTheEnd(false),
-          MostDerivedIsArrayElement(false), MostDerivedPathLength(0),
-          MostDerivedArraySize(0), MostDerivedType(T) {}
+          MostDerivedIsArrayElement(false), IsFlexibleArray(false),
+          MostDerivedPathLength(0), MostDerivedArraySize(0),
+          MostDerivedType(T) {}
 
     SubobjectDesignator(ASTContext &Ctx, const APValue &V)
         : Invalid(!V.isLValue() || !V.hasLValuePath()), IsOnePastTheEnd(false),
-          MostDerivedIsArrayElement(false), MostDerivedPathLength(0),
-          MostDerivedArraySize(0) {
+          MostDerivedIsArrayElement(false), IsFlexibleArray(false),
+          MostDerivedPathLength(0), MostDerivedArraySize(0) {
       if (!Invalid) {
         IsOnePastTheEnd = V.isLValueOnePastTheEnd();
         ArrayRef<PathEntry> VEntries = V.getLValuePath();
@@ -237,7 +241,7 @@
     bool checkSubobject(EvalInfo &Info, const Expr *E, CheckSubobjectKind CSK);
 
     /// Update this designator to refer to the first element within this array.
-    void addArrayUnchecked(const ConstantArrayType *CAT) {
+    void addArrayUnchecked(const ConstantArrayType *CAT, bool HasFlexibleArrayAttr) {
       PathEntry Entry;
       Entry.ArrayIndex = 0;
       Entries.push_back(Entry);
@@ -247,6 +251,7 @@
       MostDerivedIsArrayElement = true;
       MostDerivedArraySize = CAT->getSize().getZExtValue();
       MostDerivedPathLength = Entries.size();
+      IsFlexibleArray = HasFlexibleArrayAttr;
     }
     /// Update this designator to refer to the given base or member of this
     /// object.
@@ -1124,9 +1129,10 @@
       if (checkSubobject(Info, E, isa<FieldDecl>(D) ? CSK_Field : CSK_Base))
         Designator.addDeclUnchecked(D, Virtual);
     }
-    void addArray(EvalInfo &Info, const Expr *E, const ConstantArrayType *CAT) {
+    void addArray(EvalInfo &Info, const Expr *E, const ConstantArrayType *CAT,
+                  bool HasFlexibleArrayAttr) {
       if (checkSubobject(Info, E, CSK_ArrayToPointer))
-        Designator.addArrayUnchecked(CAT);
+        Designator.addArrayUnchecked(CAT, HasFlexibleArrayAttr);
     }
     void addComplex(EvalInfo &Info, const Expr *E, QualType EltTy, bool Imag) {
       if (checkSubobject(Info, E, Imag ? CSK_Imag : CSK_Real))
@@ -5173,9 +5179,14 @@
     }
     // The result is a pointer to the first element of the array.
     if (const ConstantArrayType *CAT
-          = Info.Ctx.getAsConstantArrayType(SubExpr->getType()))
-      Result.addArray(Info, E, CAT);
-    else
+          = Info.Ctx.getAsConstantArrayType(SubExpr->getType())) {
+      bool HasFlexibleArrayAttr = false;
+      if (auto *ME = dyn_cast<MemberExpr>(SubExpr)) {
+        auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+        HasFlexibleArrayAttr = FD && FD->hasAttr<FlexibleArrayAttr>();
+      }
+      Result.addArray(Info, E, CAT, HasFlexibleArrayAttr);
+    } else
       Result.Designator.setInvalid();
     return true;
 
@@ -5703,7 +5714,7 @@
     return false;
 
   // Get a pointer to the first element of the array.
-  Array.addArray(Info, E, ArrayType);
+  Array.addArray(Info, E, ArrayType, false);
 
   // FIXME: Perform the checks on the field types in SemaInit.
   RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl();
@@ -6017,7 +6028,7 @@
 
       // Zero-initialize all elements.
       LValue Subobject = This;
-      Subobject.addArray(Info, E, CAT);
+      Subobject.addArray(Info, E, CAT, false);
       ImplicitValueInitExpr VIE(CAT->getElementType());
       return EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE);
     }
@@ -6084,7 +6095,7 @@
   }
 
   LValue Subobject = This;
-  Subobject.addArray(Info, E, CAT);
+  Subobject.addArray(Info, E, CAT, false);
   for (unsigned Index = 0; Index != NumEltsToInit; ++Index) {
     const Expr *Init =
         Index < E->getNumInits() ? E->getInit(Index) : FillerExpr;
@@ -6134,7 +6145,7 @@
 
     // Initialize the elements.
     LValue ArrayElt = Subobject;
-    ArrayElt.addArray(Info, E, CAT);
+    ArrayElt.addArray(Info, E, CAT, false);
     for (unsigned I = 0; I != N; ++I)
       if (!VisitCXXConstructExpr(E, ArrayElt, &Value->getArrayInitializedElt(I),
                                  CAT->getElementType()) ||
@@ -6799,7 +6810,8 @@
   if (End.InvalidBase && SubobjectOnly && Type == 1 &&
       End.Designator.Entries.size() == End.Designator.MostDerivedPathLength &&
       End.Designator.MostDerivedIsArrayElement &&
-      End.Designator.MostDerivedArraySize < 2 &&
+      (End.Designator.MostDerivedArraySize < 2 ||
+       End.Designator.IsFlexibleArray) &&
       isDesignatorAtObjectEnd(Info.Ctx, End))
     return false;
 
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -3720,6 +3720,7 @@
                        RecordDecl *PrevDecl)
     : TagDecl(DK, TK, C, DC, IdLoc, Id, PrevDecl, StartLoc) {
   HasFlexibleArrayMember = false;
+  HasFlexibleArrayAttr = false;
   AnonymousStructOrUnion = false;
   HasObjectMember = false;
   HasVolatileMember = false;
Index: include/clang/Sema/AttributeList.h
===================================================================
--- include/clang/Sema/AttributeList.h
+++ include/clang/Sema/AttributeList.h
@@ -904,7 +904,8 @@
   ExpectedVariableEnumFieldOrTypedef,
   ExpectedFunctionMethodEnumOrClass,
   ExpectedStructClassVariableFunctionOrInlineNamespace,
-  ExpectedForMaybeUnused
+  ExpectedForMaybeUnused,
+  ExpectedField
 };
 
 }  // end namespace clang
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2161,6 +2161,10 @@
     : Error<"%0 attribute is not supported for this target">;
 // The err_*_attribute_argument_not_int are seperate because they're used by
 // VerifyIntegerConstantExpression.
+def err_flexible_array_attribute : Error<
+  "'flexible_array' attribute only applies to %0">;
+def err_flexible_array_nested : Error<
+  "struct with a member marked 'flexible_array' cannot be nested">;
 def err_aligned_attribute_argument_not_int : Error<
   "'aligned' attribute requires integer constant">;
 def err_align_value_attribute_argument_not_int : Error<
@@ -2512,7 +2516,8 @@
   "interface or protocol declarations|kernel functions|non-K&R-style functions|"
   "variables, enums, fields and typedefs|functions, methods, enums, and classes|"
   "structs, classes, variables, functions, and inline namespaces|"
-  "variables, functions, methods, types, enumerations, enumerators, labels, and non-static data members}1">,
+  "variables, functions, methods, types, enumerations, enumerators, labels, and non-static data members|"
+  "fields}1">,
   InGroup<IgnoredAttributes>;
 def err_attribute_wrong_decl_type : Error<warn_attribute_wrong_decl_type.Text>;
 def warn_type_attribute_wrong_type : Warning<
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -2067,6 +2067,31 @@
   }];
 }
 
+def FlexibleArrayDocs : Documentation {
+  let Category = DocCatField;
+  let Content = [{
+Use ``flexible_array`` to indicate an array member can have extra memory allocated at its end.
+This attribute can only be used on an fixed-sized array that is the last member of a struct/class and has at least one element.
+Structs or classes that have an array member marked ``flexible_array`` cannot be nested or subclassed.
+This attribute is useful when you want __builtin_object_size to be conservative
+when computing the size of an over-allocated array. For example:
+
+  .. code-block:: c
+
+    struct S0 {
+      char a[4];
+      char b[4] __attribute__((flexible_array));
+    };
+
+    // This function returns 36 instead of 4.
+    int foo() {
+      struct S0 *s0 = malloc(sizeof(S0) + 32);
+      return __builtin_object_size(s0->b, 1);
+    }
+
+  }];
+}
+
 def InternalLinkageDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -15,6 +15,7 @@
 }
 def DocCatFunction : DocumentationCategory<"Function Attributes">;
 def DocCatVariable : DocumentationCategory<"Variable Attributes">;
+def DocCatField : DocumentationCategory<"Field Attributes">;
 def DocCatType : DocumentationCategory<"Type Attributes">;
 def DocCatStmt : DocumentationCategory<"Statement Attributes">;
 // Attributes listed under the Undocumented category do not generate any public
@@ -2274,6 +2275,14 @@
   let Documentation = [LoopHintDocs, UnrollHintDocs];
 }
 
+def FlexibleArray : InheritableAttr {
+  let Spellings = [GNU<"flexible_array">,
+                   CXX11<"clang", "flexible_array">];
+  let Subjects = SubjectList<[Field], ErrorDiag,
+                             "ExpectedField">;
+  let Documentation = [FlexibleArrayDocs];
+}
+
 def CapturedRecord : InheritableAttr {
   // This attribute has no spellings as it is only ever created implicitly.
   let Spellings = [];
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -3246,6 +3246,9 @@
   /// If so, this cannot be contained in arrays or other structs as a member.
   bool HasFlexibleArrayMember : 1;
 
+  /// This is true if this struct ends with an array marked 'flexible_array'.
+  bool HasFlexibleArrayAttr : 1;
+
   /// AnonymousStructOrUnion - Whether this is the type of an anonymous struct
   /// or union.
   bool AnonymousStructOrUnion : 1;
@@ -3294,6 +3297,12 @@
   bool hasFlexibleArrayMember() const { return HasFlexibleArrayMember; }
   void setHasFlexibleArrayMember(bool V) { HasFlexibleArrayMember = V; }
 
+  bool hasFlexibleArrayAttr() const { return HasFlexibleArrayAttr; }
+  void setHasFlexibleArrayAttr(bool V) { HasFlexibleArrayAttr = V; }
+
+  bool hasFlexibleArrayMemberOrAttr() const {
+    return hasFlexibleArrayMember() || hasFlexibleArrayAttr();
+  }
   /// isAnonymousStructOrUnion - Whether this is an anonymous struct
   /// or union. To be an anonymous struct or union, it must have been
   /// declared without a name and there must be no objects of this
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to