diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1591,6 +1591,14 @@ /// kind? QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const; + /// If this variable declares a struct with a flexible array member, and + /// the flexible array member is initialized with one or more elements, + /// compute the number of bytes necessary to store those elements. + /// + /// (The standard doesn't allow initializing flexible array members; this is + /// a gcc/msvc extension.) + CharUnits getFlexibleArrayInitChars(const ASTContext &Ctx) const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K >= firstVar && K <= lastVar; } diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2720,6 +2720,21 @@ return getType().isDestructedType(); } +CharUnits VarDecl::getFlexibleArrayInitChars(const ASTContext &Ctx) const { + assert(hasInit() && "Expect initializer to check for flexible array init"); + auto *Ty = getType()->getAs(); + if (!Ty || !Ty->getDecl()->hasFlexibleArrayMember()) + return CharUnits::Zero(); + auto *List = dyn_cast(getInit()->IgnoreParens()); + if (!List) + return CharUnits::Zero(); + auto FlexibleInit = List->getInit(List->getNumInits() - 1); + auto InitTy = Ctx.getAsConstantArrayType(FlexibleInit->getType()); + if (!InitTy) + return CharUnits::Zero(); + return Ctx.getTypeSizeInChars(InitTy); +} + MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const { if (isStaticDataMember()) // FIXME: Remove ? diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -342,6 +342,8 @@ if (!Init) { if (!getLangOpts().CPlusPlus) CGM.ErrorUnsupported(D.getInit(), "constant l-value expression"); + else if (!D.getFlexibleArrayInitChars(getContext()).isZero()) + CGM.ErrorUnsupported(D.getInit(), "flexible array init"); else if (HaveInsertPoint()) { // Since we have a static initializer, this global variable can't // be constant. @@ -352,6 +354,14 @@ return GV; } +#ifndef NDEBUG + CharUnits VarSize = CGM.getContext().getTypeSizeInChars(D.getType()) + + D.getFlexibleArrayInitChars(getContext()); + CharUnits CstSize = CharUnits::fromQuantity( + CGM.getDataLayout().getTypeAllocSize(Init->getType())); + assert(VarSize == CstSize && "Emitted constant has unexpected size"); +#endif + // The initializer may differ in type from the global. Rewrite // the global to match the initializer. (We have to do this // because some types, like unions, can't be completely represented diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4614,6 +4614,8 @@ T = D->getType(); if (getLangOpts().CPlusPlus) { + if (!InitDecl->getFlexibleArrayInitChars(getContext()).isZero()) + ErrorUnsupported(D, "flexible array initializer"); Init = EmitNullConstant(T); NeedsGlobalCtor = true; } else { @@ -4627,6 +4629,14 @@ // also don't need to register a destructor. if (getLangOpts().CPlusPlus && !NeedsGlobalDtor) DelayedCXXInitPosition.erase(D); + +#ifndef NDEBUG + CharUnits VarSize = getContext().getTypeSizeInChars(ASTTy) + + InitDecl->getFlexibleArrayInitChars(getContext()); + CharUnits CstSize = CharUnits::fromQuantity( + getDataLayout().getTypeAllocSize(Init->getType())); + assert(VarSize == CstSize && "Emitted constant has unexpected size"); +#endif } } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -2004,10 +2004,6 @@ cast(InitExpr)->getNumInits() == 0) { // Empty flexible array init always allowed as an extension FlexArrayDiag = diag::ext_flexible_array_init; - } else if (SemaRef.getLangOpts().CPlusPlus) { - // Disallow flexible array init in C++; it is not required for gcc - // compatibility, and it needs work to IRGen correctly in general. - FlexArrayDiag = diag::err_flexible_array_init; } else if (!TopLevelObject) { // Disallow flexible array init on non-top-level object FlexArrayDiag = diag::err_flexible_array_init; diff --git a/clang/test/CodeGenCXX/flexible-array-init.cpp b/clang/test/CodeGenCXX/flexible-array-init.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/flexible-array-init.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -o - %s | FileCheck %s + +struct A { int x; int y[]; }; +A a = { 1, 7, 11 }; +// CHECK: @a ={{.*}} global { i32, [2 x i32] } { i32 1, [2 x i32] [i32 7, i32 11] } + +struct B { int x; int y[]; }; +B b = { 1, { 13, 15 } }; +// CHECK: @b ={{.*}} global { i32, [2 x i32] } { i32 1, [2 x i32] [i32 13, i32 15] } diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -2383,9 +2383,10 @@ static_assert(b[2].x == 3, ""); static_assert(b[2].arr[0], ""); // expected-error {{constant expression}} expected-note {{array member without known bound}} - // If we ever start to accept this, we'll need to ensure we can - // constant-evaluate it properly. - constexpr A c = {1, 2, 3}; // expected-error {{initialization of flexible array member}} + // Flexible array initialization is currently not supported by constant + // evaluation. Make sure we emit a sane error message, for now. + constexpr A c = {1, 2, 3}; // expected-warning {{flexible array initialization is a GNU extension}} + static_assert(c.arr[0] == 1, ""); // expected-error {{constant expression}} expected-note {{array member without known bound}} } void local_constexpr_var() {