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/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -364,6 +364,8 @@ "%plural{0:|: (along with %0 other memory leak%s0)}0">; def note_constexpr_unsupported_layout : Note< "type %0 has unexpected layout">; +def note_constexpr_unsupported_flexible_array : Note< + "flexible array initialization is not yet supported">; def err_experimental_clang_interp_failed : Error< "the experimental clang interpreter failed to evaluate an expression">; 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 @@ -2722,6 +2722,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/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -9980,6 +9980,17 @@ ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType()); const Expr *Init = HaveInit ? E->getInit(ElementNo++) : &VIE; + if (Field->getType()->isIncompleteArrayType()) { + if (auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType())) { + if (!CAT->getSize().isZero()) { + // Bail out for now. This might sort of "work", but the rest of the + // code isn't really prepared to handle it. + Info.FFDiag(Init, diag::note_constexpr_unsupported_flexible_array); + return false; + } + } + } + // Temporarily override This, in case there's a CXXDefaultInitExpr in here. ThisOverrideRAII ThisOverride(*Info.CurrentCall, &This, isa(Init)); 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 initializer"); 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 @@ -4615,6 +4615,8 @@ T = D->getType(); if (getLangOpts().CPlusPlus) { + if (!InitDecl->getFlexibleArrayInitChars(getContext()).isZero()) + ErrorUnsupported(D, "flexible array initializer"); Init = EmitNullConstant(T); NeedsGlobalCtor = true; } else { @@ -4628,6 +4630,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,20 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm-only -verify -DFAIL1 %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm-only -verify -DFAIL2 %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] } + +A b = { 1, { 13, 15 } }; +// CHECK: @b ={{.*}} global { i32, [2 x i32] } { i32 1, [2 x i32] [i32 13, i32 15] } + +int f(); +#ifdef FAIL1 +A c = { f(), { f(), f() } }; // expected-error {{cannot compile this flexible array initializer yet}} +#endif +#ifdef FAIL2 +void g() { + static A d = { f(), { f(), f() } }; // expected-error {{cannot compile this flexible array initializer yet}} +} +#endif 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,11 @@ 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 an error message, for now. + constexpr A c = {1, 2, 3}; // expected-error {{constexpr variable 'c' must be initialized by a constant expression}} + // expected-note@-1 {{flexible array initialization is not yet supported}} + // expected-warning@-2 {{flexible array initialization is a GNU extension}} } void local_constexpr_var() {