[clang] [Clang][Sema]: Allow flexible arrays in unions and alone in structs (PR #84428)

Kees Cook via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 7 20:49:17 PST 2024


https://github.com/kees created https://github.com/llvm/llvm-project/pull/84428

GNU and MSVC have extensions where flexible array members (or their equivalent) can be in unions or alone in structs. This is already fully supported in Clang through the 0-sized array ("fake flexible array") extension or when C99 flexible array members have been syntactically obfuscated.

Provide a feature flag to explicitly allow these extensions directly for C99 flexible arrays, since they are common code patterns in active use by the Linux kernel (and other projects). Such projects have been using either 0-sized arrays (which is considered deprecated in favor of C99 flexible array members) or via obfuscated syntax, both of which complicate their code bases.

For example, these do not error by default:

union one {
	int a;
	int b[0];
};

union two {
	int a;
	struct {
		struct { } __empty;
		int b[];
	};
};

But this does:

union three {
	int a;
	int b[];
};

Add -fflex-array-extensions so that C99 flexible array members can be fully supported in these scenarios without errors, allowing for a seamless transition for code bases away from 0-sized arrays without losing existing code patterns.

>From ce31f1d75f060b32e5dbc5756fe41cc8eaac83a6 Mon Sep 17 00:00:00 2001
From: Kees Cook <keescook at chromium.org>
Date: Thu, 7 Mar 2024 17:03:09 -0800
Subject: [PATCH] [Clang][Sema]: Allow flexible arrays in unions and alone in
 structs

GNU and MSVC have extensions where flexible array members (or their
equivalent) can be in unions or alone in structs. This is already fully
supported in Clang through the 0-sized array ("fake flexible array")
extension or when C99 flexible array members have been syntactically
obfuscated.

Provide a feature flag to explicitly allow these extensions directly
for C99 flexible arrays, since they are common code patterns in active
use by the Linux kernel (and other projects). Such projects have been
using either 0-sized arrays (which is considered deprecated in favor
of C99 flexible array members) or via obfuscated syntax, both of which
complicate their code bases.

For example, these do not error by default:

union one {
	int a;
	int b[0];
};

union two {
	int a;
	struct {
		struct { } __empty;
		int b[];
	};
};

But this does:

union three {
	int a;
	int b[];
};

Add -fflex-array-extensions so that C99 flexible array members can
be fully supported in these scenarios without errors, allowing for a
seamless transition for code bases away from 0-sized arrays without
losing existing code patterns.
---
 clang/docs/ReleaseNotes.rst               |  6 ++++
 clang/include/clang/Basic/LangOptions.def |  1 +
 clang/include/clang/Driver/Options.td     |  5 +++
 clang/lib/Driver/ToolChains/Clang.cpp     |  1 +
 clang/lib/Sema/SemaDecl.cpp               | 19 +++++-----
 clang/test/Sema/flex-array-extensions.c   | 43 +++++++++++++++++++++++
 6 files changed, 66 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/Sema/flex-array-extensions.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 1b901a27fd19d1..c15ff98c60b8b2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -164,6 +164,12 @@ New Compiler Flags
   This diagnostic can be disabled to make ``-Wmissing-field-initializers`` behave
   like it did before Clang 18.x. Fixes (`#56628 <https://github.com/llvm/llvm-project/issues/68933>`_)
 
+- ``-fflex-array-extensions``. Allows for the use of extensions to flexible array members
+  syntax so they can be directly used in unions and alone in structs. This construction is
+  already fully supported by Clang under certain conditions, so this provide support for
+  codebases that would otherwise not be able to migrate from 0-sized "fake" flexible array
+  members to C99 flexible array members.
+
 Deprecated Compiler Flags
 -------------------------
 
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 2b42b521a30363..c5c810f63d93d1 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -453,6 +453,7 @@ LANGOPT(MatrixTypes, 1, 0, "Enable or disable the builtin matrix type")
 ENUM_LANGOPT(StrictFlexArraysLevel, StrictFlexArraysLevelKind, 2,
              StrictFlexArraysLevelKind::Default,
              "Rely on strict definition of flexible arrays")
+COMPATIBLE_LANGOPT(FlexArrayExtensions, 1, 0, "Enable flexible array extensions")
 
 COMPATIBLE_VALUE_LANGOPT(MaxTokens, 32, 0, "Max number of tokens per TU or 0")
 
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index bef38738fde82e..bde7ba456fd27d 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2819,6 +2819,11 @@ defm fine_grained_bitfield_accesses : BoolOption<"f", "fine-grained-bitfield-acc
   BothFlags<[], [ClangOption, CC1Option]>>,
   Group<f_clang_Group>;
 
+def fflex_array_extensions : Flag<["-"], "fflex-array-extensions">, Group<f_Group>,
+  Visibility<[ClangOption, CC1Option]>,
+  HelpText<"Enable flexible array extensions from GNU and MSVC">,
+  MarshallingInfoFlag<LangOpts<"FlexArrayExtensions">>;
+
 def fexperimental_relative_cxx_abi_vtables :
   Flag<["-"], "fexperimental-relative-c++-abi-vtables">,
   Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index fa17f6295d6ea7..356129b452514b 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6663,6 +6663,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
                   options::OPT_fno_unroll_loops);
 
   Args.AddLastArg(CmdArgs, options::OPT_fstrict_flex_arrays_EQ);
+  Args.AddLastArg(CmdArgs, options::OPT_fflex_array_extensions);
 
   Args.AddLastArg(CmdArgs, options::OPT_pthread);
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 67e56a917a51de..1fd6200d4876e4 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -19355,17 +19355,18 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
           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;
+          DiagID =
+              getLangOpts().FlexArrayExtensions ? 0
+              : 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
+          DiagID = getLangOpts().FlexArrayExtensions ? 0
+                   : getLangOpts().MicrosoftExt
                        ? diag::ext_flexible_array_empty_aggregate_ms
-                       : getLangOpts().CPlusPlus
-                             ? diag::ext_flexible_array_empty_aggregate_gnu
-                             : diag::err_flexible_array_empty_aggregate;
+                   : getLangOpts().CPlusPlus
+                       ? diag::ext_flexible_array_empty_aggregate_gnu
+                       : diag::err_flexible_array_empty_aggregate;
 
         if (DiagID)
           Diag(FD->getLocation(), DiagID)
diff --git a/clang/test/Sema/flex-array-extensions.c b/clang/test/Sema/flex-array-extensions.c
new file mode 100644
index 00000000000000..2464714d7a55ab
--- /dev/null
+++ b/clang/test/Sema/flex-array-extensions.c
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 %s -verify=c -fsyntax-only -fflex-array-extensions
+
+// The test checks that flexible array members do not emit warnings when
+// -fflex-array-extensions when used in a union or alone in a structure.
+
+struct already_hidden {
+	int a;
+	union {
+		int b;
+		struct {
+			struct { } __empty;
+			char array[];
+		};
+	};
+};
+
+struct still_zero_sized {
+	struct { } __unused;
+	int array[];
+};
+
+struct no_warn1 {
+	int a;
+	union {
+		int b;
+		char array[];
+	};
+};
+
+struct no_warn2 {
+	int array[];
+};
+
+union no_warn3 {
+	short array[];
+};
+
+struct still_illegal {
+	int array[]; // c-error {{flexible array member 'array' with type 'int[]' is not at the end of struct}}
+	int a;       // c-note {{next field declaration is here}}
+};
+
+// expected-no-diagnostics



More information about the cfe-commits mailing list