Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -369,6 +369,7 @@ def ObjCPointerIntrospectPerformSelector : DiagGroup<"deprecated-objc-pointer-introspection-performSelector">; def ObjCPointerIntrospect : DiagGroup<"deprecated-objc-pointer-introspection", [ObjCPointerIntrospectPerformSelector]>; def ObjCMultipleMethodNames : DiagGroup<"objc-multiple-method-names">; +def ObjCFlexibleArray : DiagGroup<"objc-flexible-array">; def OpenCLUnsupportedRGBA: DiagGroup<"opencl-unsupported-rgba">; def DeprecatedObjCIsaUsage : DiagGroup<"deprecated-objc-isa-usage">; def ExplicitInitializeCall : DiagGroup<"explicit-initialize-call">; @@ -743,6 +744,7 @@ VolatileRegisterVar, ObjCMissingSuperCalls, ObjCDesignatedInit, + ObjCFlexibleArray, OverloadedVirtual, PrivateExtern, SelTypeCast, Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5219,6 +5219,28 @@ def ext_flexible_array_union_gnu : Extension< "flexible array member %0 in a union is a GNU extension">, InGroup; +def err_flexible_array_not_at_end : Error< + "flexible array member %0 with type %1 is not at the end of" + " %select{struct|interface|union|class|enum}2">; +def err_objc_variable_sized_type_not_at_end : Error< + "field %0 with variable sized type %1 is not at the end of class">; +def note_next_field_declaration : Note< + "next field declaration is here">; +def note_next_ivar_declaration : Note< + "next %select{instance variable declaration|synthesized instance variable}0" + " is here">; +def err_synthesize_variable_sized_ivar : Error< + "synthesized property with variable size type %0" + " requires an existing instance variable">; +def err_flexible_array_arc_retainable : Error< + "ARC forbids flexible array members with retainable object type">; +def warn_variable_sized_ivar_visibility : Warning< + "field %0 with variable sized type %1 is not visible to subclasses and" + " can conflict with their instance variables">, InGroup; +def warn_superclass_variable_sized_type_not_at_end : Warning< + "field %0 can overwrite instance variable %1 with variable sized type %2" + " in superclass %3">, InGroup; + let CategoryName = "ARC Semantic Issue" in { // ARC-mode diagnostics. Index: clang/lib/CodeGen/CGObjCMac.cpp =================================================================== --- clang/lib/CodeGen/CGObjCMac.cpp +++ clang/lib/CodeGen/CGObjCMac.cpp @@ -5089,6 +5089,11 @@ fieldType = arrayType->getElementType(); } + if (auto arrayType = CGM.getContext().getAsIncompleteArrayType(fieldType)) { + numElts = 0; + fieldType = arrayType->getElementType(); + } + assert(!fieldType->isArrayType() && "ivar of non-constant array type?"); // If we ended up with a zero-sized array, we've done what we can do within Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -14946,6 +14946,7 @@ // possibly recursively, a member that is such a structure) // shall not be a member of a structure or an element of an // array. + bool IsLastField = (i + 1 == Fields.end()); if (FDTy->isFunctionType()) { // Field declared as a function. Diag(FD->getLocation(), diag::err_field_declared_as_function) @@ -14953,60 +14954,70 @@ FD->setInvalidDecl(); EnclosingDecl->setInvalidDecl(); continue; - } else if (FDTy->isIncompleteArrayType() && Record && - ((i + 1 == Fields.end() && !Record->isUnion()) || - ((getLangOpts().MicrosoftExt || - getLangOpts().CPlusPlus) && - (i + 1 == Fields.end() || Record->isUnion())))) { - // Flexible array member. - // Microsoft and g++ is more permissive regarding flexible array. - // It will accept flexible array in union and also - // as the sole element of a struct/class. - unsigned DiagID = 0; - if (Record->isUnion()) - DiagID = getLangOpts().MicrosoftExt - ? diag::ext_flexible_array_union_ms - : getLangOpts().CPlusPlus - ? diag::ext_flexible_array_union_gnu - : diag::err_flexible_array_union; - else if (NumNamedMembers < 1) - DiagID = getLangOpts().MicrosoftExt - ? diag::ext_flexible_array_empty_aggregate_ms - : getLangOpts().CPlusPlus - ? diag::ext_flexible_array_empty_aggregate_gnu - : diag::err_flexible_array_empty_aggregate; - - if (DiagID) - Diag(FD->getLocation(), DiagID) << FD->getDeclName() - << Record->getTagKind(); - // While the layout of types that contain virtual bases is not specified - // by the C++ standard, both the Itanium and Microsoft C++ ABIs place - // virtual bases after the derived members. This would make a flexible - // array member declared at the end of an object not adjacent to the end - // of the type. - if (CXXRecordDecl *RD = dyn_cast(Record)) - if (RD->getNumVBases() != 0) - Diag(FD->getLocation(), diag::err_flexible_array_virtual_base) + } else if (FDTy->isIncompleteArrayType() && + (Record || isa(EnclosingDecl))) { + if (Record) { + // Flexible array member. + // Microsoft and g++ is more permissive regarding flexible array. + // It will accept flexible array in union and also + // as the sole element of a struct/class. + unsigned DiagID = 0; + if (!Record->isUnion() && !IsLastField) { + Diag(FD->getLocation(), diag::err_flexible_array_not_at_end) + << FD->getDeclName() << FD->getType() << Record->getTagKind(); + Diag((*(i + 1))->getLocation(), diag::note_next_field_declaration); + FD->setInvalidDecl(); + EnclosingDecl->setInvalidDecl(); + continue; + } else if (Record->isUnion()) + DiagID = getLangOpts().MicrosoftExt + ? diag::ext_flexible_array_union_ms + : getLangOpts().CPlusPlus + ? diag::ext_flexible_array_union_gnu + : diag::err_flexible_array_union; + else if (NumNamedMembers < 1) + DiagID = getLangOpts().MicrosoftExt + ? diag::ext_flexible_array_empty_aggregate_ms + : getLangOpts().CPlusPlus + ? diag::ext_flexible_array_empty_aggregate_gnu + : diag::err_flexible_array_empty_aggregate; + + if (DiagID) + Diag(FD->getLocation(), DiagID) << FD->getDeclName() + << Record->getTagKind(); + // While the layout of types that contain virtual bases is not specified + // by the C++ standard, both the Itanium and Microsoft C++ ABIs place + // virtual bases after the derived members. This would make a flexible + // array member declared at the end of an object not adjacent to the end + // of the type. + if (CXXRecordDecl *RD = dyn_cast(Record)) + if (RD->getNumVBases() != 0) + Diag(FD->getLocation(), diag::err_flexible_array_virtual_base) + << FD->getDeclName() << Record->getTagKind(); + if (!getLangOpts().C99) + Diag(FD->getLocation(), diag::ext_c99_flexible_array_member) << FD->getDeclName() << Record->getTagKind(); - if (!getLangOpts().C99) - Diag(FD->getLocation(), diag::ext_c99_flexible_array_member) - << FD->getDeclName() << Record->getTagKind(); - // If the element type has a non-trivial destructor, we would not - // implicitly destroy the elements, so disallow it for now. - // - // FIXME: GCC allows this. We should probably either implicitly delete - // the destructor of the containing class, or just allow this. - QualType BaseElem = Context.getBaseElementType(FD->getType()); - if (!BaseElem->isDependentType() && BaseElem.isDestructedType()) { - Diag(FD->getLocation(), diag::err_flexible_array_has_nontrivial_dtor) - << FD->getDeclName() << FD->getType(); - FD->setInvalidDecl(); - EnclosingDecl->setInvalidDecl(); - continue; + // If the element type has a non-trivial destructor, we would not + // implicitly destroy the elements, so disallow it for now. + // + // FIXME: GCC allows this. We should probably either implicitly delete + // the destructor of the containing class, or just allow this. + QualType BaseElem = Context.getBaseElementType(FD->getType()); + if (!BaseElem->isDependentType() && BaseElem.isDestructedType()) { + Diag(FD->getLocation(), diag::err_flexible_array_has_nontrivial_dtor) + << FD->getDeclName() << FD->getType(); + FD->setInvalidDecl(); + EnclosingDecl->setInvalidDecl(); + continue; + } + // Okay, we have a legal flexible array member at the end of the struct. + Record->setHasFlexibleArrayMember(true); + } else { + // In ObjCContainerDecl ivars with incomplete array type are accepted, + // unless they are followed by another ivar. That check is done + // elsewhere, after synthesized ivars are known. } - // Okay, we have a legal flexible array member at the end of the struct. - Record->setHasFlexibleArrayMember(true); } else if (!FDTy->isDependentType() && RequireCompleteType(FD->getLocation(), FD->getType(), diag::err_field_incomplete)) { @@ -15023,7 +15034,7 @@ // 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()) + if (!IsLastField) Diag(FD->getLocation(), diag::ext_variable_sized_type_in_struct) << FD->getDeclName() << FD->getType(); else { Index: clang/lib/Sema/SemaDeclObjC.cpp =================================================================== --- clang/lib/Sema/SemaDeclObjC.cpp +++ clang/lib/Sema/SemaDeclObjC.cpp @@ -3717,6 +3717,26 @@ } } +/// Diagnose attempts to use flexible array member with retainable object type. +static void DiagnoseRetainableFlexibleArrayMember(Sema &S, + ObjCInterfaceDecl *ID) { + if (!S.getLangOpts().ObjCAutoRefCount) + return; + + for (auto ivar = ID->all_declared_ivar_begin(); ivar; + ivar = ivar->getNextIvar()) { + if (ivar->isInvalidDecl()) + continue; + QualType IvarTy = ivar->getType(); + if (IvarTy->isIncompleteArrayType() && + (IvarTy.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) && + IvarTy->isObjCLifetimeType()) { + S.Diag(ivar->getLocation(), diag::err_flexible_array_arc_retainable); + ivar->setInvalidDecl(); + } + } +} + Sema::ObjCContainerKind Sema::getObjCContainerKind() const { switch (CurContext->getDeclKind()) { case Decl::ObjCInterface: @@ -3737,6 +3757,96 @@ } } +static bool IsVariableSizedType(QualType T) { + if (T->isIncompleteArrayType()) + return true; + const auto *RecordTy = T->getAs(); + return (RecordTy && RecordTy->getDecl()->hasFlexibleArrayMember()); +} + +static void DiagnoseVariableSizedIvars(Sema &S, ObjCContainerDecl *OCD) { + ObjCInterfaceDecl *IntfDecl = nullptr; + ObjCInterfaceDecl::ivar_range Ivars = llvm::make_range( + ObjCInterfaceDecl::ivar_iterator(), ObjCInterfaceDecl::ivar_iterator()); + if ((IntfDecl = dyn_cast(OCD))) { + Ivars = IntfDecl->ivars(); + } else if (auto *ImplDecl = dyn_cast(OCD)) { + IntfDecl = ImplDecl->getClassInterface(); + Ivars = ImplDecl->ivars(); + } else if (auto *CategoryDecl = dyn_cast(OCD)) { + if (CategoryDecl->IsClassExtension()) { + IntfDecl = CategoryDecl->getClassInterface(); + Ivars = CategoryDecl->ivars(); + } + } + + // Check if variable sized ivar is in interface and visible to subclasses. + if (!isa(OCD)) { + for (auto ivar : Ivars) { + if (!ivar->isInvalidDecl() && IsVariableSizedType(ivar->getType())) { + S.Diag(ivar->getLocation(), diag::warn_variable_sized_ivar_visibility) + << ivar->getDeclName() << ivar->getType(); + } + } + } + + // Subsequent checks require interface decl. + if (!IntfDecl) + return; + + // Check if variable sized ivar is followed by another ivar. + for (ObjCIvarDecl *ivar = IntfDecl->all_declared_ivar_begin(); ivar; + ivar = ivar->getNextIvar()) { + if (ivar->isInvalidDecl() || !ivar->getNextIvar()) + continue; + QualType IvarTy = ivar->getType(); + bool IsInvalidIvar = false; + if (IvarTy->isIncompleteArrayType()) { + S.Diag(ivar->getLocation(), diag::err_flexible_array_not_at_end) + << ivar->getDeclName() << IvarTy + << TTK_Class; // Use "class" for Obj-C. + IsInvalidIvar = true; + } else if (const RecordType *RecordTy = IvarTy->getAs()) { + if (RecordTy->getDecl()->hasFlexibleArrayMember()) { + S.Diag(ivar->getLocation(), + diag::err_objc_variable_sized_type_not_at_end) + << ivar->getDeclName() << IvarTy; + IsInvalidIvar = true; + } + } + if (IsInvalidIvar) { + S.Diag(ivar->getNextIvar()->getLocation(), + diag::note_next_ivar_declaration) + << ivar->getNextIvar()->getSynthesize(); + ivar->setInvalidDecl(); + } + } + + // Check if ObjC container adds ivars after variable sized ivar in superclass. + // Perform the check only if OCD is the first container to declare ivars to + // avoid multiple warnings for the same ivar. + ObjCIvarDecl *FirstIvar = + (Ivars.begin() == Ivars.end()) ? nullptr : *Ivars.begin(); + if (FirstIvar && (FirstIvar == IntfDecl->all_declared_ivar_begin())) { + const ObjCInterfaceDecl *SuperClass = IntfDecl->getSuperClass(); + while (SuperClass && SuperClass->ivar_empty()) + SuperClass = SuperClass->getSuperClass(); + if (SuperClass) { + auto IvarIter = SuperClass->ivar_begin(); + std::advance(IvarIter, SuperClass->ivar_size() - 1); + const ObjCIvarDecl *LastIvar = *IvarIter; + if (IsVariableSizedType(LastIvar->getType())) { + S.Diag(FirstIvar->getLocation(), + diag::warn_superclass_variable_sized_type_not_at_end) + << FirstIvar->getDeclName() << LastIvar->getDeclName() + << LastIvar->getType() << SuperClass->getDeclName(); + S.Diag(LastIvar->getLocation(), diag::note_entity_declared_at) + << LastIvar->getDeclName(); + } + } + } +} + // Note: For class/category implementations, allMethods is always null. Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef allMethods, ArrayRef allTUVars) { @@ -3868,6 +3978,7 @@ if (IDecl->hasDesignatedInitializers()) DiagnoseMissingDesignatedInitOverrides(IC, IDecl); DiagnoseWeakIvars(*this, IC); + DiagnoseRetainableFlexibleArrayMember(*this, IDecl); bool HasRootClassAttr = IDecl->hasAttr(); if (IDecl->getSuperClass() == nullptr) { @@ -3937,6 +4048,7 @@ } } } + DiagnoseVariableSizedIvars(*this, OCD); if (isInterfaceDeclKind) { // Reject invalid vardecls. for (unsigned i = 0, e = allTUVars.size(); i != e; i++) { Index: clang/lib/Sema/SemaObjCProperty.cpp =================================================================== --- clang/lib/Sema/SemaObjCProperty.cpp +++ clang/lib/Sema/SemaObjCProperty.cpp @@ -1290,6 +1290,14 @@ // An abstract type is as bad as an incomplete type. CompleteTypeErr = true; } + if (!CompleteTypeErr) { + const RecordType *RecordTy = PropertyIvarType->getAs(); + if (RecordTy && RecordTy->getDecl()->hasFlexibleArrayMember()) { + Diag(PropertyIvarLoc, diag::err_synthesize_variable_sized_ivar) + << PropertyIvarType; + CompleteTypeErr = true; // suppress later diagnostics about the ivar + } + } if (CompleteTypeErr) Ivar->setInvalidDecl(); ClassImpDecl->addDecl(Ivar); Index: clang/test/CodeGenObjC/ivar-layout-flexible-array.m =================================================================== --- /dev/null +++ clang/test/CodeGenObjC/ivar-layout-flexible-array.m @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -Wno-objc-root-class -fobjc-arc -emit-llvm -o - %s | FileCheck %s + +// rdar://problem/21054495 +typedef char FlexibleArray[]; + +@interface FlexibleArrayMember { + FlexibleArray flexible_array; +} +@end +@implementation FlexibleArrayMember +@end +// CHECK: @OBJC_METH_VAR_NAME_{{.*}} = private unnamed_addr constant {{.*}} c"flexible_array\00" +// CHECK-NEXT: @OBJC_METH_VAR_TYPE_{{.*}} = private unnamed_addr constant {{.*}} c"^c\00" + + +struct Packet { + int size; + FlexibleArray data; +}; + +@interface VariableSizeIvar { + struct Packet flexible_struct; +} +@end +@implementation VariableSizeIvar +@end +// CHECK: @OBJC_METH_VAR_NAME_{{.*}} = private unnamed_addr constant {{.*}} c"flexible_struct\00" +// CHECK-NEXT: @OBJC_METH_VAR_TYPE_{{.*}} = private unnamed_addr constant {{.*}} c"{Packet=\22size\22i\22data\22[0c]}\00" Index: clang/test/Sema/transparent-union.c =================================================================== --- clang/test/Sema/transparent-union.c +++ clang/test/Sema/transparent-union.c @@ -102,7 +102,7 @@ union pr30520v { void b; } __attribute__((transparent_union)); // expected-error {{field has incomplete type 'void'}} -union pr30520a { int b[]; } __attribute__((transparent_union)); // expected-error {{field has incomplete type 'int []'}} +union pr30520a { int b[]; } __attribute__((transparent_union)); // expected-error {{flexible array member 'b' in a union is not allowed}} // expected-note@+1 2 {{forward declaration of 'struct stb'}} union pr30520s { struct stb b; } __attribute__((transparent_union)); // expected-error {{field has incomplete type 'struct stb'}} Index: clang/test/SemaCXX/flexible-array-test.cpp =================================================================== --- clang/test/SemaCXX/flexible-array-test.cpp +++ clang/test/SemaCXX/flexible-array-test.cpp @@ -66,6 +66,11 @@ char c[]; }; +class C { + char c[]; // expected-error {{flexible array member 'c' with type 'char []' is not at the end of class}} + int s; // expected-note {{next field declaration is here}} +}; + namespace rdar9065507 { struct StorageBase { Index: clang/test/SemaObjC/flexible-array-arc.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/flexible-array-arc.m @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class -DNOARC %s +#ifdef NOARC +// expected-no-diagnostics +#endif + +@interface RetainableArray { + id flexible[]; +#ifndef NOARC + // expected-error@-2 {{ARC forbids flexible array members with retainable object type}} +#endif +} +@end +@implementation RetainableArray +@end + +// Emit diagnostic only if have @implementation. +@interface RetainableArrayWithoutImpl { + id flexible[]; +} +@end + +// With ARC flexible array member objects can be only __unsafe_unretained +@interface UnsafeUnretainedArray { + __unsafe_unretained id flexible[]; +} +@end +@implementation UnsafeUnretainedArray +@end + +@interface NotObjCLifetimeTypeArray { + char flexible[]; +} +@end +@implementation NotObjCLifetimeTypeArray +@end Index: clang/test/SemaObjC/flexible-array.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/flexible-array.m @@ -0,0 +1,288 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s + +// # Flexible array member. +// ## Instance variables only in interface. +@interface LastIvar { + char flexible[]; +} +@end + +@interface NotLastIvar { + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +// ## Instance variables in implementation. +@interface LastIvarInImpl +@end +@implementation LastIvarInImpl { + char flexible[]; // expected-warning {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}} +} +@end + +@interface NotLastIvarInImpl +@end +@implementation NotLastIvarInImpl { + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} + // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}} + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +@implementation NotLastIvarInImplWithoutInterface { // expected-warning {{cannot find interface declaration for 'NotLastIvarInImplWithoutInterface'}} + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} + // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}} + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +@interface LastIvarInClass_OtherIvarInImpl { + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} +} +@end +@implementation LastIvarInClass_OtherIvarInImpl { + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +// ## Non-instance variables in implementation. +@interface LastIvarInClass_UnrelatedVarInImpl { + char flexible[]; +} +@end +@implementation LastIvarInClass_UnrelatedVarInImpl +int nonIvar; +@end + +// ## Instance variables in class extension. +@interface LastIvarInExtension +@end +@interface LastIvarInExtension() { + char flexible[]; // expected-warning {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}} +} +@end + +@interface NotLastIvarInExtension +@end +@interface NotLastIvarInExtension() { + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} + // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}} + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +@interface LastIvarInClass_OtherIvarInExtension { + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} +} +@end +@interface LastIvarInClass_OtherIvarInExtension() { + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +@interface LastIvarInExtension_OtherIvarInExtension +@end +@interface LastIvarInExtension_OtherIvarInExtension() { + int last; // expected-note {{next instance variable declaration is here}} +} +@end +@interface LastIvarInExtension_OtherIvarInExtension() +// Extension without ivars to test we see through such extensions. +@end +@interface LastIvarInExtension_OtherIvarInExtension() { + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} + // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}} +} +@end + +@interface LastIvarInExtension_OtherIvarInImpl +@end +@interface LastIvarInExtension_OtherIvarInImpl() { + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} + // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}} +} +@end +@implementation LastIvarInExtension_OtherIvarInImpl { + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +// ## Instance variables in named categories. +@interface IvarInNamedCategory +@end +@interface IvarInNamedCategory(Category) { + char flexible[]; // expected-error {{instance variables may not be placed in categories}} +} +@end + +// ## Synthesized instance variable. +@interface LastIvarAndProperty { + char _flexible[]; +} +@property char flexible[]; // expected-error {{property cannot have array or function type 'char []'}} +@end + +// ## Synthesize other instance variables. +@interface LastIvar_ExplicitlyNamedPropertyBackingIvarPreceding { + int _elementsCount; + char flexible[]; +} +@property int count; +@end +@implementation LastIvar_ExplicitlyNamedPropertyBackingIvarPreceding +@synthesize count = _elementsCount; +@end + +@interface LastIvar_ImplicitlyNamedPropertyBackingIvarPreceding { + int count; + char flexible[]; +} +@property int count; +@end +@implementation LastIvar_ImplicitlyNamedPropertyBackingIvarPreceding +@synthesize count; +@end + +@interface NotLastIvar_ExplicitlyNamedPropertyBackingIvarLast { + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} +} +@property int count; +@end +@implementation NotLastIvar_ExplicitlyNamedPropertyBackingIvarLast +@synthesize count = _elementsCount; // expected-note {{next synthesized instance variable is here}} +@end + +@interface NotLastIvar_ImplicitlyNamedPropertyBackingIvarLast { + char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}} +} +@property int count; // expected-note {{next synthesized instance variable is here}} +@end +@implementation NotLastIvar_ImplicitlyNamedPropertyBackingIvarLast +// Test auto-synthesize. +//@synthesize count; +@end + + +// # Variable sized types. +struct Packet { + unsigned int size; + char data[]; +}; + +// ## Instance variables only in interface. +@interface LastStructIvar { + struct Packet flexible; +} +@end + +@interface NotLastStructIvar { + struct Packet flexible; // expected-error {{field 'flexible' with variable sized type 'struct Packet' is not at the end of class}} + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +// ## Instance variables in implementation. +@interface LastStructIvarInImpl +@end +@implementation LastStructIvarInImpl { + struct Packet flexible; // expected-warning {{field 'flexible' with variable sized type 'struct Packet' is not visible to subclasses and can conflict with their instance variables}} +} +@end + +@interface NotLastStructIvarInImpl +@end +@implementation NotLastStructIvarInImpl { + struct Packet flexible; // expected-error {{field 'flexible' with variable sized type 'struct Packet' is not at the end of class}} + // expected-warning@-1 {{field 'flexible' with variable sized type 'struct Packet' is not visible to subclasses and can conflict with their instance variables}} + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +@interface LastStructIvarInClass_OtherIvarInImpl { + struct Packet flexible; // expected-error {{field 'flexible' with variable sized type 'struct Packet' is not at the end of class}} +} +@end +@implementation LastStructIvarInClass_OtherIvarInImpl { + int last; // expected-note {{next instance variable declaration is here}} +} +@end + +// ## Synthesized instance variable. +@interface LastSynthesizeStructIvar +@property int first; +@property struct Packet flexible; // expected-error {{synthesized property with variable size type 'struct Packet' requires an existing instance variable}} +@end +@implementation LastSynthesizeStructIvar +@end + +@interface NotLastSynthesizeStructIvar +@property struct Packet flexible; // expected-error {{synthesized property with variable size type 'struct Packet' requires an existing instance variable}} +@property int last; +@end +@implementation NotLastSynthesizeStructIvar +@end + +@interface LastStructIvarWithExistingIvarAndSynthesizedProperty { + struct Packet _flexible; +} +@property struct Packet flexible; +@end +@implementation LastStructIvarWithExistingIvarAndSynthesizedProperty +@end + + +// # Subclasses. +@interface FlexibleArrayMemberBase { + char flexible[]; // expected-note6 {{'flexible' declared here}} +} +@end + +@interface NoIvarAdditions : FlexibleArrayMemberBase +@end +@implementation NoIvarAdditions +@end + +@interface AddedIvarInInterface : FlexibleArrayMemberBase { + int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}} +} +@end + +@interface AddedIvarInImplementation : FlexibleArrayMemberBase +@end +@implementation AddedIvarInImplementation { + int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}} +} +@end + +@interface AddedIvarInExtension : FlexibleArrayMemberBase +@end +@interface AddedIvarInExtension() { + int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}} +} +@end + +@interface SynthesizedIvar : FlexibleArrayMemberBase +@property int count; +@end +@implementation SynthesizedIvar +@synthesize count; // expected-warning {{field 'count' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}} +@end + +@interface WarnInSubclassOnlyOnce : FlexibleArrayMemberBase { + int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}} +} +@end +@interface WarnInSubclassOnlyOnce() { + int laster; +} +@end +@implementation WarnInSubclassOnlyOnce { + int lastest; +} +@end + +@interface AddedIvarInSubSubClass : NoIvarAdditions { + int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}} +} +@end Index: clang/test/SemaObjC/ivar-sem-check-1.m =================================================================== --- clang/test/SemaObjC/ivar-sem-check-1.m +++ clang/test/SemaObjC/ivar-sem-check-1.m @@ -6,14 +6,15 @@ @interface INTF { struct F {} JJ; - int arr[]; // expected-error {{field has incomplete type}} + int arr[]; // expected-error {{flexible array member 'arr' with type 'int []' is not at the end of class}} struct S IC; // expected-error {{field has incomplete type}} + // expected-note@-1 {{next instance variable declaration is here}} struct T { // expected-note {{previous definition is here}} struct T {} X; // expected-error {{nested redefinition of 'T'}} }YYY; FOO BADFUNC; // expected-error {{field 'BADFUNC' declared as a function}} int kaka; // expected-note {{previous declaration is here}} int kaka; // expected-error {{duplicate member 'kaka'}} - char ch[]; // expected-error {{field has incomplete type}} + char ch[]; } @end Index: clang/test/SemaObjCXX/flexible-array.mm =================================================================== --- /dev/null +++ clang/test/SemaObjCXX/flexible-array.mm @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s + +// Test only flexible array member functionality specific to C++. + +union VariableSizeUnion { + int s; + char c[]; +}; + +@interface LastUnionIvar { + VariableSizeUnion flexible; +} +@end + +@interface NotLastUnionIvar { + VariableSizeUnion flexible; // expected-error {{field 'flexible' with variable sized type 'VariableSizeUnion' is not at the end of class}} + int last; // expected-note {{next instance variable declaration is here}} +} +@end + + +class VariableSizeClass { +public: + int s; + char c[]; +}; + +@interface LastClassIvar { + VariableSizeClass flexible; +} +@end + +@interface NotLastClassIvar { + VariableSizeClass flexible; // expected-error {{field 'flexible' with variable sized type 'VariableSizeClass' is not at the end of class}} + int last; // expected-note {{next instance variable declaration is here}} +} +@end