[clang] [C] Do not diagnose flexible array members with -Wdefault-const-init-field-unsafe (PR #140578)
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Tue May 20 10:05:49 PDT 2025
https://github.com/AaronBallman updated https://github.com/llvm/llvm-project/pull/140578
>From 625662da41256345c4609835754c3a61018b54ca Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Mon, 19 May 2025 13:09:03 -0400
Subject: [PATCH 1/2] [C] Do not diagnose flexible array members with
-Wdefault-const-init-field-unsafe
This addresses post-commit review feedback from someone who discovered
that we diagnosed code like the following:
struct S {
int len;
const char fam[];
} s;
despite it being invalid to initialize the flexible array member.
Note, this applies to flexible array members and zero-sized arrays at
the end of a structure (an old-style flexible array member), but it
does not apply to one-sized arrays at the end of a structure because
those do occupy storage that can be initialized.
---
clang/lib/Sema/SemaInit.cpp | 8 ++++++
clang/test/Sema/warn-default-const-init.c | 30 +++++++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 9ee8603ff7811..e17e68966dc00 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -6513,6 +6513,14 @@ static bool canPerformArrayCopy(const InitializedEntity &Entity) {
static const FieldDecl *getConstField(const RecordDecl *RD) {
assert(!isa<CXXRecordDecl>(RD) && "Only expect to call this in C mode");
for (const FieldDecl *FD : RD->fields()) {
+ // If the field is a flexible array member, we don't want to consider it
+ // as a const field because there's no way to initialize the FAM anyway.
+ if (Decl::isFlexibleArrayMemberLike(
+ FD->getASTContext(), FD, FD->getType(),
+ LangOptions::StrictFlexArraysLevelKind::ZeroOrIncomplete,
+ /*IgnoreTemplateOrMacroSubstitution=*/true))
+ continue;
+
QualType QT = FD->getType();
if (QT.isConstQualified())
return FD;
diff --git a/clang/test/Sema/warn-default-const-init.c b/clang/test/Sema/warn-default-const-init.c
index e788d72899685..e6ff0aa783e23 100644
--- a/clang/test/Sema/warn-default-const-init.c
+++ b/clang/test/Sema/warn-default-const-init.c
@@ -85,3 +85,33 @@ void func() {
static const int b; // zero-init-var-warning {{default initialization of an object of type 'const int' is incompatible with C++}} \
cxx-error {{default initialization of an object of const type 'const int'}}
}
+
+// Test the behavior of flexible array members. Those cannot be initialized
+// when a stack-allocated object of the structure type is created. We handle
+// degenerate flexible arrays similarly, but only if the array does not
+// actually specify any storage. Note that C++ does not have flexible array
+// members at all, which is why the test is disabled there.
+#ifndef __cplusplus
+struct RealFAM {
+ int len;
+ const char fam[];
+};
+
+struct FakeFAM {
+ int len;
+ const char fam[0];
+};
+
+struct NotTreatedAsAFAM {
+ int len;
+ const char fam[1]; // unsafe-field-note {{member 'fam' declared 'const' here}} \
+ unsafe-field-compat-note {{member 'fam' declared 'const' here}}
+};
+
+void test_fams() {
+ struct RealFAM One;
+ struct FakeFAM Two;
+ struct NotTreatedAsAFAM Three; // unsafe-field-warning {{default initialization of an object of type 'struct NotTreatedAsAFAM' with const member leaves the object uninitialized}} \
+ unsafe-field-compat-warning {{default initialization of an object of type 'struct NotTreatedAsAFAM' with const member leaves the object uninitialized and is incompatible with C++}}
+}
+#endif // !defined(__cplusplus)
>From 2637a020d818a205d3be933f73f1449a0870faf0 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Tue, 20 May 2025 08:22:19 -0400
Subject: [PATCH 2/2] Base behavior on -fstrict-flex-arrays
---
clang/lib/Sema/SemaInit.cpp | 5 +++--
clang/test/Sema/warn-default-const-init.c | 23 +++++++++++++----------
2 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index e17e68966dc00..ddadfa0ac3490 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -6515,9 +6515,10 @@ static const FieldDecl *getConstField(const RecordDecl *RD) {
for (const FieldDecl *FD : RD->fields()) {
// If the field is a flexible array member, we don't want to consider it
// as a const field because there's no way to initialize the FAM anyway.
+ const ASTContext &Ctx = FD->getASTContext();
if (Decl::isFlexibleArrayMemberLike(
- FD->getASTContext(), FD, FD->getType(),
- LangOptions::StrictFlexArraysLevelKind::ZeroOrIncomplete,
+ Ctx, FD, FD->getType(),
+ Ctx.getLangOpts().getStrictFlexArraysLevel(),
/*IgnoreTemplateOrMacroSubstitution=*/true))
continue;
diff --git a/clang/test/Sema/warn-default-const-init.c b/clang/test/Sema/warn-default-const-init.c
index e6ff0aa783e23..a70a0ccb6ebc6 100644
--- a/clang/test/Sema/warn-default-const-init.c
+++ b/clang/test/Sema/warn-default-const-init.c
@@ -1,23 +1,26 @@
// Both of these should enable everything.
-// RUN: %clang_cc1 -fsyntax-only -verify=unsafe-var-compat,unsafe-field-compat,zero-init-var,zero-init-field -Wc++-compat -Wno-tentative-definition-compat %s
-// RUN: %clang_cc1 -fsyntax-only -verify=unsafe-var,unsafe-field,zero-init-var,zero-init-field -Wdefault-const-init %s
+// RUN: %clang_cc1 -fsyntax-only -verify=unsafe-var-compat,unsafe-field-compat,zero-init-var,zero-init-field -Wc++-compat -Wno-tentative-definition-compat -fstrict-flex-arrays=2 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=unsafe-var,unsafe-field,zero-init-var,zero-init-field,unsafe-field-restricted-flex -Wdefault-const-init -fstrict-flex-arrays=2 %s
// This should enable nothing.
// RUN: %clang_cc1 -fsyntax-only -verify=good -Wno-default-const-init-unsafe %s
// Only unsafe field and variable diagnostics
-// RUN: %clang_cc1 -fsyntax-only -verify=unsafe-var,unsafe-field %s
-// RUN: %clang_cc1 -fsyntax-only -verify=unsafe-var,unsafe-field -Wdefault-const-init-unsafe %s
+// RUN: %clang_cc1 -fsyntax-only -verify=unsafe-var,unsafe-field,unsafe-field-restricted-flex -fstrict-flex-arrays=2 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=unsafe-var,unsafe-field,unsafe-field-restricted-flex -Wdefault-const-init-unsafe -fstrict-flex-arrays=2 %s
// Only zero init field and variable diagnostics
// RUN: %clang_cc1 -fsyntax-only -verify=zero-init-var,zero-init-field -Wdefault-const-init -Wno-default-const-init-unsafe %s
// Only zero init and unsafe field diagnostics
-// RUN: %clang_cc1 -fsyntax-only -verify=zero-init-field,unsafe-field -Wno-default-const-init-var-unsafe -Wdefault-const-init-field %s
+// RUN: %clang_cc1 -fsyntax-only -verify=zero-init-field,unsafe-field,unsafe-field-restricted-flex -Wno-default-const-init-var-unsafe -Wdefault-const-init-field -fstrict-flex-arrays=2 %s
// Only zero init and unsafe variable diagnostics
// RUN: %clang_cc1 -fsyntax-only -verify=zero-init-var,unsafe-var -Wno-default-const-init-field-unsafe -Wdefault-const-init-var %s
+// Only unsafe field diagnostics, but with flexible arrays set to any kind of array
+// RUN: %clang_cc1 -fsyntax-only -verify=unsafe-field -Wno-default-const-init-var-unsafe -Wdefault-const-init-field-unsafe -fstrict-flex-arrays=0 %s
+
// C++ tests
// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s
@@ -88,9 +91,9 @@ void func() {
// Test the behavior of flexible array members. Those cannot be initialized
// when a stack-allocated object of the structure type is created. We handle
-// degenerate flexible arrays similarly, but only if the array does not
-// actually specify any storage. Note that C++ does not have flexible array
-// members at all, which is why the test is disabled there.
+// degenerate flexible arrays based on -fstrict-flex-arrays. Note that C++ does
+// not have flexible array members at all, which is why the test is disabled
+// there.
#ifndef __cplusplus
struct RealFAM {
int len;
@@ -104,14 +107,14 @@ struct FakeFAM {
struct NotTreatedAsAFAM {
int len;
- const char fam[1]; // unsafe-field-note {{member 'fam' declared 'const' here}} \
+ const char fam[1]; // unsafe-field-restricted-flex-note {{member 'fam' declared 'const' here}} \
unsafe-field-compat-note {{member 'fam' declared 'const' here}}
};
void test_fams() {
struct RealFAM One;
struct FakeFAM Two;
- struct NotTreatedAsAFAM Three; // unsafe-field-warning {{default initialization of an object of type 'struct NotTreatedAsAFAM' with const member leaves the object uninitialized}} \
+ struct NotTreatedAsAFAM Three; // unsafe-field-restricted-flex-warning {{default initialization of an object of type 'struct NotTreatedAsAFAM' with const member leaves the object uninitialized}} \
unsafe-field-compat-warning {{default initialization of an object of type 'struct NotTreatedAsAFAM' with const member leaves the object uninitialized and is incompatible with C++}}
}
#endif // !defined(__cplusplus)
More information about the cfe-commits
mailing list