efriedma created this revision.
efriedma added reviewers: aaron.ballman, rsmith, erichkeane.
Herald added a project: All.
efriedma requested review of this revision.
Herald added a project: clang.
Flexible array initialization is a C/C++ extension implemented in many
compilers to allow initializing the flexible array tail of a struct type that
contains a flexible array. In clang, this is currently restricted to C. But
this construct is used in the Microsoft SDK headers, so I'd like to extend it
to C++.
For now, this doesn't handle dynamic initialization; probably not hard to
implement, but it's extra code, and I don't think it's necessary for the
expected uses. And I'm not handling constant evaluation; it should fail
gracefully.
I've added some additional code to assert that flexible array init works the
way we expect it to in both C and C++.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D123649
Files:
clang/include/clang/AST/Decl.h
clang/lib/AST/Decl.cpp
clang/lib/CodeGen/CGDecl.cpp
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/Sema/SemaInit.cpp
clang/test/CodeGenCXX/flexible-array-init.cpp
clang/test/SemaCXX/constant-expression-cxx11.cpp
Index: clang/test/SemaCXX/constant-expression-cxx11.cpp
===================================================================
--- clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ 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() {
Index: clang/test/CodeGenCXX/flexible-array-init.cpp
===================================================================
--- /dev/null
+++ 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] }
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -2004,10 +2004,6 @@
cast<InitListExpr>(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;
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ 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
}
}
Index: clang/lib/CodeGen/CGDecl.cpp
===================================================================
--- clang/lib/CodeGen/CGDecl.cpp
+++ 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
Index: clang/lib/AST/Decl.cpp
===================================================================
--- clang/lib/AST/Decl.cpp
+++ 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<RecordType>();
+ if (!Ty || !Ty->getDecl()->hasFlexibleArrayMember())
+ return CharUnits::Zero();
+ auto *List = dyn_cast<InitListExpr>(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 ?
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ 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; }
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits