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 Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -15,6 +15,8 @@ } def DocCatFunction : DocumentationCategory<"Function Attributes">; def DocCatVariable : DocumentationCategory<"Variable Attributes">; +def DocCatField : + DocumentationCategory<"Non-static Data Member Attributes">; def DocCatType : DocumentationCategory<"Type Attributes">; def DocCatStmt : DocumentationCategory<"Statement Attributes">; // Attributes listed under the Undocumented category do not generate any public @@ -2275,6 +2277,13 @@ let Documentation = [LoopHintDocs, UnrollHintDocs]; } +def FlexibleArray : InheritableAttr { + let Spellings = [GNU<"flexible_array">, + CXX11<"clang", "flexible_array">]; + let Subjects = SubjectList<[Field], ErrorDiag>; + let Documentation = [FlexibleArrayDocs]; +} + def CapturedRecord : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly. let Spellings = []; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -2069,6 +2069,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/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2165,6 +2165,12 @@ : 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 %select{" + "the last member of a struct|members of structs or classes|" + "fixed sized array members|array members that have at least one element}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< @@ -2516,7 +2522,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|" + "non-static data members}1">, InGroup; def err_attribute_wrong_decl_type : Error; def warn_type_attribute_wrong_type : Warning< 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: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3718,6 +3718,7 @@ RecordDecl *PrevDecl) : TagDecl(DK, TK, C, DC, IdLoc, Id, PrevDecl, StartLoc) { HasFlexibleArrayMember = false; + HasFlexibleArrayAttr = false; AnonymousStructOrUnion = false; HasObjectMember = false; HasVolatileMember = 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. unsigned 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 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. @@ -1134,9 +1139,10 @@ if (checkSubobject(Info, E, isa(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 = false) { 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)) @@ -5183,9 +5189,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 (const auto *ME = dyn_cast(SubExpr)) { + const auto *FD = dyn_cast(ME->getMemberDecl()); + HasFlexibleArrayAttr = FD && FD->hasAttr(); + } + Result.addArray(Info, E, CAT, HasFlexibleArrayAttr); + } else Result.Designator.setInvalid(); return true; @@ -6809,7 +6820,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/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -688,27 +688,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(AT)) { - if (CAT->getSize().ugt(1)) - return false; - } else if (!isa(AT)) - return false; - - E = E->IgnoreParens(); - - // A flexible array member must be the last member in the class. - if (const auto *ME = dyn_cast(E)) { + if (const auto *ME = dyn_cast(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(ME->getMemberDecl())) { - RecordDecl::field_iterator FI( - DeclContext::decl_iterator(const_cast(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(AT)) { + RecordDecl::field_iterator FI( + DeclContext::decl_iterator(const_cast(FD))); + if (std::next(FI) == FD->getParent()->field_end() && + CAT->getSize().ule(1)) + return true; + } } - } return false; } Index: lib/CodeGen/CodeGenTBAA.cpp =================================================================== --- lib/CodeGen/CodeGenTBAA.cpp +++ lib/CodeGen/CodeGenTBAA.cpp @@ -180,7 +180,7 @@ if (const RecordType *TTy = QTy->getAs()) { 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()) { 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/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -13919,6 +13919,16 @@ continue; } + // Set HasFlexibleArrayAttr. Only the last member of a struct can be marked + // flexible_array. + if (FD->hasAttr()) { + if (i + 1 == end) + Record->setHasFlexibleArrayAttr(true); + else + Diag(FD->getLocation(), diag::err_flexible_array_attribute) << 0; + } + + // C99 6.7.2.1p2: // A structure or union shall not contain a member with // incomplete or function type (hence, a structure shall not @@ -13998,24 +14008,27 @@ EnclosingDecl->setInvalidDecl(); continue; } else if (const RecordType *FDTTy = FDTy->getAs()) { - 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(EnclosingDecl) && RequireNonAbstractType(FD->getLocation(), FD->getType(), Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1803,6 +1803,30 @@ Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } +static void handleFlexibleArrayAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + const auto *FD = cast(D); + const auto *CAT = dyn_cast(FD->getType().getTypePtr()); + + if (FD->getParent()->isUnion()) { + S.Diag(Attr.getLoc(), diag::err_flexible_array_attribute) << 1; + return; + } + + if (!CAT) { + S.Diag(Attr.getLoc(), diag::err_flexible_array_attribute) << 2; + return; + } + + if (CAT->getSize().ult(1)) { + S.Diag(Attr.getLoc(), diag::err_flexible_array_attribute) << 3; + 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(S, D, Attr.getRange(), @@ -5665,6 +5689,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/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/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -13251,7 +13251,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()) { - if (VTTy->getDecl()->hasFlexibleArrayMember()) { + if (VTTy->getDecl()->hasFlexibleArrayMemberOrAttr()) { if (Diagnose) { if (IsBlock) S.Diag(Loc, diag::err_ref_flexarray_type); 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/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: 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: 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/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 non-static data members}} + +struct S0 { + int a[4]; + int foo1() __attribute__((flexible_array)); // expected-error {{'flexible_array' attribute only applies to non-static data members}} +}; + +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 +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: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -2641,6 +2641,7 @@ case GenericRecord: return "(S.getLangOpts().CPlusPlus ? ExpectedStructOrUnionOrClass : " "ExpectedStructOrUnion)"; + case Field: return "ExpectedField"; case Func | ObjCMethod | Block: return "ExpectedFunctionMethodOrBlock"; case Func | ObjCMethod | Class: return "ExpectedFunctionMethodOrClass"; case Func | Param: