[clang] 5d8aaad - [C2x] Implement support for empty brace initialization (WG14 N2900 and WG14 N3011)

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 3 12:23:01 PDT 2023


Author: Aaron Ballman
Date: 2023-04-03T15:22:52-04:00
New Revision: 5d8aaad4452f60ba8902e921d9bed606713a8f26

URL: https://github.com/llvm/llvm-project/commit/5d8aaad4452f60ba8902e921d9bed606713a8f26
DIFF: https://github.com/llvm/llvm-project/commit/5d8aaad4452f60ba8902e921d9bed606713a8f26.diff

LOG: [C2x] Implement support for empty brace initialization (WG14 N2900 and WG14 N3011)

This implements support for allowing {} to consistently zero initialize
objects. We already supported most of this work as a GNU extension, but
the C2x feature goes beyond what the GNU extension allowed.

The changes in this patch are:

* Removed the -Wgnu-empty-initializer warning group. The extension is
  now a C2x extension warning instead. Note that use of
  `-Wno-gnu-empty-initializer seems` to be quite low in the wild
(https://sourcegraph.com/search?q=context%3Aglobal+-file%3A.*test.*+%22-Wno-gnu-empty-initializer%22&patternType=standard&sm=1&groupBy=repo
  which currently only gives 8 hits total), so this is not expected to
  be an overly disruptive change. But I'm adding the clang vendors
  review group just in case this expectation is wrong.
* Reworded the diagnostic wording to be about a C2x extension, added a
  pre-C2x compat warning.
* Allow {} to zero initialize a VLA

This functionality is exposed as an extension in all older C modes
(same as the GNU extension was), but does *not* allow the extension for
VLA initialization in C++ due to concern about handling non-trivially
constructible types.

Differential Revision: https://reviews.llvm.org/D147349

Added: 
    clang/test/C/C2x/n2900_n3011.c
    clang/test/C/C2x/n2900_n3011_2.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticCommonKinds.td
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/lib/CodeGen/CGExprAgg.cpp
    clang/lib/Parse/ParseInit.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaInit.cpp
    clang/test/Sema/array-init.c
    clang/test/Sema/complex-init-list.c
    clang/test/Sema/compound-literal.c
    clang/test/Sema/flexible-array-init.c
    clang/test/Sema/gnu-flags.c
    clang/test/Sema/sizeless-1.c
    clang/test/Sema/vla.c
    clang/test/SemaObjC/property.m
    clang/test/SemaOpenCL/intel-subgroup-avc-ext-types.cl
    clang/www/c_status.html

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cd9aae998a503..53a0541ed290a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -121,6 +121,18 @@ C2x Feature Support
   which introduces the ``bool``, ``static_assert``, ``alignas``, ``alignof``,
   and ``thread_local`` keywords in C2x.
 
+- Implemented `WG14 N2900 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2900.htm>`_
+  and `WG14 N3011 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3011.htm>`_
+  which allows for empty braced initialization in C.
+
+  .. code-block:: c
+
+    struct S { int x, y } s = {}; // Initializes s.x and s.y to 0
+
+  As part of this change, the ``-Wgnu-empty-initializer`` warning group was
+  removed, as this is no longer a GNU extension but a C2x extension. You can
+  use ``-Wno-c2x-extensions`` to silence the extension warning instead.
+
 Non-comprehensive list of changes in this release
 -------------------------------------------------
 - Clang now saves the address of ABI-indirect function parameters on the stack,

diff  --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index f574b0f0171b7..bac77299671c5 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -130,6 +130,12 @@ def warn_cxx20_compat_consteval : Warning<
 def warn_missing_type_specifier : Warning<
   "type specifier missing, defaults to 'int'">,
   InGroup<ImplicitInt>, DefaultIgnore;
+
+def ext_c_empty_initializer : Extension<
+  "use of an empty initializer is a C2x extension">, InGroup<C2x>;
+def warn_c2x_compat_empty_initializer : Warning<
+  "use of an empty initializer is incompatible with C standards before C2x">,
+  InGroup<CPre2xCompat>, DefaultIgnore;
 }
 
 let CategoryName = "Nullability Issue" in {

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 0d2829d64501f..31f64f4eceb7c 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -256,7 +256,6 @@ def EmptyBody : DiagGroup<"empty-body">;
 def Exceptions : DiagGroup<"exceptions">;
 def DeclarationAfterStatement : DiagGroup<"declaration-after-statement">;
 
-def GNUEmptyInitializer : DiagGroup<"gnu-empty-initializer">;
 def GNUEmptyStruct : DiagGroup<"gnu-empty-struct">;
 def ExtraTokens : DiagGroup<"extra-tokens">;
 def CXX98CompatExtraSemi : DiagGroup<"c++98-compat-extra-semi">;
@@ -1135,7 +1134,7 @@ def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct,
                             GNUBinaryLiteral, GNUCaseRange,
                             GNUComplexInteger, GNUCompoundLiteralInitializer,
                             GNUConditionalOmittedOperand, GNUDesignator,
-                            GNUEmptyInitializer, GNUEmptyStruct,
+                            GNUEmptyStruct,
                             VLAExtension, GNUFlexibleArrayInitializer,
                             GNUFlexibleArrayUnionMember, GNUFoldingConstant,
                             GNUImaginaryConstant, GNUIncludeNext,

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 929ba9e3287ef..d8ad04728b32d 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -182,8 +182,6 @@ def ext_gnu_statement_expr_macro : Extension<
   InGroup<GNUStatementExpressionFromMacroExpansion>;
 def ext_gnu_conditional_expr : Extension<
   "use of GNU ?: conditional expression extension, omitting middle operand">, InGroup<GNUConditionalOmittedOperand>;
-def ext_gnu_empty_initializer : Extension<
-  "use of GNU empty initializer extension">, InGroup<GNUEmptyInitializer>;
 def ext_gnu_array_range : Extension<"use of GNU array range extension">,
   InGroup<GNUDesignator>;
 def ext_gnu_missing_equal_designator : ExtWarn<

diff  --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 33d2295fa98ee..7a2d65084887a 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -1658,11 +1658,19 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
   LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), ExprToVisit->getType());
 
   // Handle initialization of an array.
-  if (ExprToVisit->getType()->isArrayType()) {
+  if (ExprToVisit->getType()->isConstantArrayType()) {
     auto AType = cast<llvm::ArrayType>(Dest.getAddress().getElementType());
     EmitArrayInit(Dest.getAddress(), AType, ExprToVisit->getType(), ExprToVisit,
                   InitExprs, ArrayFiller);
     return;
+  } else if (ExprToVisit->getType()->isVariableArrayType()) {
+    // A variable array type that has an initializer can only do empty
+    // initialization. And because this feature is not exposed as an extension
+    // in C++, we can safely memset the array memory to zero.
+    assert(InitExprs.size() == 0 &&
+           "you can only use an empty initializer with VLAs");
+    CGF.EmitNullInitialization(Dest.getAddress(), ExprToVisit->getType());
+    return;
   }
 
   assert(ExprToVisit->getType()->isRecordType() &&

diff  --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp
index af0c3b47958d2..e9eb56b6583a3 100644
--- a/clang/lib/Parse/ParseInit.cpp
+++ b/clang/lib/Parse/ParseInit.cpp
@@ -429,7 +429,7 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
 ///       initializer: [C99 6.7.8]
 ///         '{' initializer-list '}'
 ///         '{' initializer-list ',' '}'
-/// [GNU]   '{' '}'
+/// [C2x]   '{' '}'
 ///
 ///       initializer-list:
 ///         designation[opt] initializer ...[opt]
@@ -447,9 +447,12 @@ ExprResult Parser::ParseBraceInitializer() {
   ExprVector InitExprs;
 
   if (Tok.is(tok::r_brace)) {
-    // Empty initializers are a C++ feature and a GNU extension to C.
-    if (!getLangOpts().CPlusPlus)
-      Diag(LBraceLoc, diag::ext_gnu_empty_initializer);
+    // Empty initializers are a C++ feature and a GNU extension to C before C2x.
+    if (!getLangOpts().CPlusPlus) {
+      Diag(LBraceLoc, getLangOpts().C2x
+                          ? diag::warn_c2x_compat_empty_initializer
+                          : diag::ext_c_empty_initializer);
+    }
     // Match the '}'.
     return Actions.ActOnInitList(LBraceLoc, std::nullopt, ConsumeBrace());
   }

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 40c2a993b8ffe..47b548e9a9fc6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7491,10 +7491,23 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
             SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd())))
       return ExprError();
     if (literalType->isVariableArrayType()) {
-      if (!tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc,
-                                           diag::err_variable_object_no_init)) {
+      // C2x 6.7.9p4: An entity of variable length array type shall not be
+      // initialized except by an empty initializer.
+      //
+      // The C extension warnings are issued from ParseBraceInitializer() and
+      // do not need to be issued here. However, we continue to issue an error
+      // in the case there are initializers or we are compiling C++. We allow
+      // use of VLAs in C++, but it's not clear we want to allow {} to zero
+      // init a VLA in C++ in all cases (such as with non-trivial constructors).
+      // FIXME: should we allow this construct in C++ when it makes sense to do
+      // so?
+      std::optional<unsigned> NumInits;
+      if (const auto *ILE = dyn_cast<InitListExpr>(LiteralExpr))
+        NumInits = ILE->getNumInits();
+      if ((LangOpts.CPlusPlus || NumInits.value_or(0)) &&
+          !tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc,
+                                           diag::err_variable_object_no_init))
         return ExprError();
-      }
     }
   } else if (!literalType->isDependentType() &&
              RequireCompleteType(LParenLoc, literalType,

diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 569024551d559..1775dad77d345 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -1565,20 +1565,23 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity,
                                       unsigned &StructuredIndex) {
   if (Index >= IList->getNumInits()) {
     if (!VerifyOnly) {
-      if (DeclType->isSizelessBuiltinType())
-        SemaRef.Diag(IList->getBeginLoc(),
-                     SemaRef.getLangOpts().CPlusPlus11
-                         ? diag::warn_cxx98_compat_empty_sizeless_initializer
-                         : diag::err_empty_sizeless_initializer)
-            << DeclType << IList->getSourceRange();
-      else
-        SemaRef.Diag(IList->getBeginLoc(),
-                     SemaRef.getLangOpts().CPlusPlus11
-                         ? diag::warn_cxx98_compat_empty_scalar_initializer
-                         : diag::err_empty_scalar_initializer)
-            << IList->getSourceRange();
+      if (SemaRef.getLangOpts().CPlusPlus) {
+        if (DeclType->isSizelessBuiltinType())
+          SemaRef.Diag(IList->getBeginLoc(),
+                       SemaRef.getLangOpts().CPlusPlus11
+                           ? diag::warn_cxx98_compat_empty_sizeless_initializer
+                           : diag::err_empty_sizeless_initializer)
+              << DeclType << IList->getSourceRange();
+        else
+          SemaRef.Diag(IList->getBeginLoc(),
+                       SemaRef.getLangOpts().CPlusPlus11
+                           ? diag::warn_cxx98_compat_empty_scalar_initializer
+                           : diag::err_empty_scalar_initializer)
+              << IList->getSourceRange();
+      }
     }
-    hadError = !SemaRef.getLangOpts().CPlusPlus11;
+    hadError =
+        SemaRef.getLangOpts().CPlusPlus && !SemaRef.getLangOpts().CPlusPlus11;
     ++Index;
     ++StructuredIndex;
     return;
@@ -1908,11 +1911,24 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
     // Check for VLAs; in standard C it would be possible to check this
     // earlier, but I don't know where clang accepts VLAs (gcc accepts
     // them in all sorts of strange places).
-    if (!VerifyOnly)
-      SemaRef.Diag(VAT->getSizeExpr()->getBeginLoc(),
-                   diag::err_variable_object_no_init)
-          << VAT->getSizeExpr()->getSourceRange();
-    hadError = true;
+    bool HasErr = IList->getNumInits() != 0 || SemaRef.getLangOpts().CPlusPlus;
+    if (!VerifyOnly) {
+      // C2x 6.7.9p4: An entity of variable length array type shall not be
+      // initialized except by an empty initializer.
+      //
+      // The C extension warnings are issued from ParseBraceInitializer() and
+      // do not need to be issued here. However, we continue to issue an error
+      // in the case there are initializers or we are compiling C++. We allow
+      // use of VLAs in C++, but it's not clear we want to allow {} to zero
+      // init a VLA in C++ in all cases (such as with non-trivial constructors).
+      // FIXME: should we allow this construct in C++ when it makes sense to do
+      // so?
+      if (HasErr)
+        SemaRef.Diag(VAT->getSizeExpr()->getBeginLoc(),
+                     diag::err_variable_object_no_init)
+            << VAT->getSizeExpr()->getSourceRange();
+    }
+    hadError = HasErr;
     ++Index;
     ++StructuredIndex;
     return;

diff  --git a/clang/test/C/C2x/n2900_n3011.c b/clang/test/C/C2x/n2900_n3011.c
new file mode 100644
index 0000000000000..b0de79e9a516f
--- /dev/null
+++ b/clang/test/C/C2x/n2900_n3011.c
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -std=c2x -fsyntax-only -Wpre-c2x-compat -verify=compat %s
+// RUN: %clang_cc1 -std=c17 -fsyntax-only -pedantic -Wno-comment -verify=pedantic %s
+
+/* WG14 N2900: yes
+ * Consistent, Warningless, and Intuitive Initialization with {}
+ */
+
+/* WG14 N3011: yes
+ * Consistent, Warningless, and Intuitive Initialization with {}
+ */
+void test(void) {
+  struct S { int x, y; } s = {}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+                                    pedantic-warning {{use of an empty initializer is a C2x extension}}
+  int i = {}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+                                  pedantic-warning {{use of an empty initializer is a C2x extension}}
+  int j = (int){}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+                      pedantic-warning {{use of an empty initializer is a C2x extension}}
+  int unknown_size[] = {}; // pedantic-warning {{zero size arrays are an extension}} \
+                              pedantic-warning {{use of an empty initializer is a C2x extension}} \
+                              compat-warning {{use of an empty initializer is incompatible with C standards before C2x}}
+  int vla[i] = {}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+                      pedantic-warning {{use of an empty initializer is a C2x extension}}
+  int *compound_literal_vla = (int[i]){}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+                                             pedantic-warning {{use of an empty initializer is a C2x extension}}
+
+  struct T {
+	int i;
+    struct S s;
+  } t1 = { 1, {} }; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+                       pedantic-warning {{use of an empty initializer is a C2x extension}}
+
+  struct T t2 = {
+    1, {
+      2, {} // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+               pedantic-warning {{use of an empty initializer is a C2x extension}}
+    }
+  };
+
+  struct T t3 = {
+    (int){}, // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+                pedantic-warning {{use of an empty initializer is a C2x extension}}
+    {} // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+          pedantic-warning {{use of an empty initializer is a C2x extension}}
+  };
+
+  // Ensure that zero initialization does what you'd expect in a constant expr.
+  // FIXME: the "not an ICE" warning is incorrect for C2x, but we don't yet
+  // implement WG14 N3038.
+  _Static_assert((int){} == 0, "what?");  // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
+                                             pedantic-warning {{use of an empty initializer is a C2x extension}} \
+                                             pedantic-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+}
+

diff  --git a/clang/test/C/C2x/n2900_n3011_2.c b/clang/test/C/C2x/n2900_n3011_2.c
new file mode 100644
index 0000000000000..eb15fbf905c86
--- /dev/null
+++ b/clang/test/C/C2x/n2900_n3011_2.c
@@ -0,0 +1,122 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2x -verify -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+struct S { int x, y; };
+struct T {
+  int i;
+  struct S s;
+};
+
+// CHECK: @[[CONST_T1:.+]] = private unnamed_addr constant %struct.T { i32 1, %struct.S zeroinitializer }
+// CHECK: @[[CONST_T2:.+]] = private unnamed_addr constant %struct.T { i32 1, %struct.S { i32 2, i32 0 } }
+
+void test_struct() {
+  struct S s = {};
+  // CHECK: define {{.*}} void @test_struct
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT: %[[S:.+]] = alloca %struct.S
+  // CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[S]], i8 0, i64 8, i1 false)
+}
+
+void test_var() {
+  int i = {};
+  // CHECK: define {{.*}} void @test_var
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT: %[[I:.+]] = alloca i32
+  // CHECK-NEXT: store i32 0, ptr %[[I]]
+}
+
+void test_simple_compound_literal() {
+  int j = (int){};
+  // CHECK: define {{.*}} void @test_simple_compound_literal
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT: %[[J:.+]] = alloca i32
+  // CHECK-NEXT: %[[COMPOUND:.+]] = alloca i32
+  // CHECK-NEXT: store i32 0, ptr %[[COMPOUND]]
+  // CHECK-NEXT: %[[MEM:.+]] = load i32, ptr %[[COMPOUND]]
+  // CHECK-NEXT: store i32 %[[MEM]], ptr %[[J]]
+}
+
+void test_zero_size_array() {
+  int unknown_size[] = {};
+  // CHECK: define {{.*}} void @test_zero_size_array
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT: %[[UNKNOWN:.+]] = alloca [0 x i32]
+}
+
+void test_vla() {
+  int num_elts = 12;
+  int vla[num_elts] = {};
+  // CHECK: define {{.*}} void @test_vla
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT: %[[NUM_ELTS_PTR:.+]] = alloca i32
+  // CHECK: %[[VLA_EXPR:.+]] = alloca i64
+  // CHECK-NEXT: store i32 12, ptr %[[NUM_ELTS_PTR]]
+  // CHECK-NEXT: %[[NUM_ELTS:.+]] = load i32, ptr %[[NUM_ELTS_PTR]]
+  // CHECK-NEXT: %[[NUM_ELTS_EXT:.+]] = zext i32 %[[NUM_ELTS]] to i64
+  // CHECK: %[[VLA:.+]] = alloca i32, i64 %[[NUM_ELTS_EXT]]
+  // CHECK-NEXT: store i64 %[[NUM_ELTS_EXT]], ptr %[[VLA_EXPR]]
+  // CHECK-NEXT: %[[BYTES_TO_COPY:.+]] = mul nuw i64 %[[NUM_ELTS_EXT]], 4
+  // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[VLA]], i8 0, i64 %[[BYTES_TO_COPY]], i1 false)
+}
+
+void test_zero_size_vla() {
+  int num_elts = 0;
+  int vla[num_elts] = {};
+  // CHECK: define {{.*}} void @test_zero_size_vla
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT: %[[NUM_ELTS_PTR:.+]] = alloca i32
+  // CHECK: %[[VLA_EXPR:.+]] = alloca i64
+  // CHECK-NEXT: store i32 0, ptr %[[NUM_ELTS_PTR]]
+  // CHECK-NEXT: %[[NUM_ELTS:.+]] = load i32, ptr %[[NUM_ELTS_PTR]]
+  // CHECK-NEXT: %[[NUM_ELTS_EXT:.+]] = zext i32 %[[NUM_ELTS]] to i64
+  // CHECK: %[[VLA:.+]] = alloca i32, i64 %[[NUM_ELTS_EXT]]
+  // CHECK-NEXT: store i64 %[[NUM_ELTS_EXT]], ptr %[[VLA_EXPR]]
+  // CHECK-NEXT: %[[BYTES_TO_COPY:.+]] = mul nuw i64 %[[NUM_ELTS_EXT]], 4
+  // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[VLA]], i8 0, i64 %[[BYTES_TO_COPY]], i1 false)
+}
+
+void test_compound_literal_vla() {
+  int num_elts = 12;
+  int *compound_literal_vla = (int[num_elts]){};
+  // CHECK: define {{.*}} void @test_compound_literal_vla
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT: %[[NUM_ELTS_PTR:.+]] = alloca i32
+  // CHECK-NEXT: %[[COMP_LIT_VLA:.+]] = alloca ptr
+  // CHECK-NEXT: %[[COMP_LIT:.+]] = alloca i32
+  // CHECK-NEXT: store i32 12, ptr %[[NUM_ELTS_PTR]]
+  // CHECK-NEXT: %[[NUM_ELTS:.+]] = load i32, ptr %[[NUM_ELTS_PTR]]
+  // CHECK-NEXT: %[[NUM_ELTS_EXT:.+]] = zext i32 %[[NUM_ELTS]] to i64
+  // CHECK-NEXT: %[[BYTES_TO_COPY:.+]] = mul nuw i64 %[[NUM_ELTS_EXT]], 4
+  // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[COMP_LIT]], i8 0, i64 %[[BYTES_TO_COPY]], i1 false)
+  // CHECK-NEXT: store ptr %[[COMP_LIT]], ptr %[[COMP_LIT_VLA]]
+}
+
+void test_nested_structs() {
+  struct T t1 = { 1, {} };
+  struct T t2 = { 1, { 2, {} } };
+  struct T t3 = { (int){}, {} };
+  // CHECK: define {{.*}} void @test_nested_structs
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT: %[[T1:.+]] = alloca %struct.T
+  // CHECK-NEXT: %[[T2:.+]] = alloca %struct.T
+  // CHECK-NEXT: %[[T3:.+]] = alloca %struct.T
+  // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}} %[[T1]], ptr {{.*}} @[[CONST_T1]], i64 12, i1 false)
+  // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}} %[[T2]], ptr {{.*}} @[[CONST_T2]], i64 12, i1 false)
+  // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[T3]], i8 0, i64 12, i1 false)
+}
+
+void test_vla_of_nested_structs(int num_elts) {
+  struct T t3[num_elts] = {};
+  // CHECK: define {{.*}} void @test_vla_of_nested_structs(i32 noundef %[[NUM_ELTS_PARAM:.+]])
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT: %[[NUM_ELTS_PTR:.+]] = alloca i32
+  // CHECK: %[[VLA_EXPR:.+]] = alloca i64
+  // CHECK-NEXT: store i32 %[[NUM_ELTS_PARAM]], ptr %[[NUM_ELTS_PTR]]
+  // CHECK-NEXT: %[[NUM_ELTS_LOCAL:.+]] = load i32, ptr %[[NUM_ELTS_PTR]]
+  // CHECK-NEXT: %[[NUM_ELTS_EXT:.+]] = zext i32 %[[NUM_ELTS_LOCAL]] to i64
+  // CHECK: %[[VLA:.+]] = alloca %struct.T, i64 %[[NUM_ELTS_EXT]]
+  // CHECK-NEXT: store i64 %[[NUM_ELTS_EXT]], ptr %[[VLA_EXPR]]
+  // CHECK-NEXT: %[[COPY_BYTES:.+]] = mul nuw i64 %[[NUM_ELTS_EXT]], 12
+  // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[VLA]], i8 0, i64 %[[COPY_BYTES]], i1 false)
+}

diff  --git a/clang/test/Sema/array-init.c b/clang/test/Sema/array-init.c
index fcc3c13bc91da..0e23a7d3d3755 100644
--- a/clang/test/Sema/array-init.c
+++ b/clang/test/Sema/array-init.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=gnu99 -fsyntax-only -pedantic -verify %s
+// RUN: %clang_cc1 -std=gnu99 -fsyntax-only -pedantic -verify=expected,pedantic %s
 // RUN: %clang_cc1 -std=gnu99 -fsyntax-only -Wgnu -Wc11-extensions -verify %s
 // REQUIRES: LP64
 
@@ -64,7 +64,7 @@ void test(void) {
     { 2, 4, 6 },
     { 3, 5, 7 },
     { 4, 6, 8 },
-    {  }, // expected-warning{{use of GNU empty initializer extension}} expected-warning{{excess elements in array initializer}}
+    {  }, // pedantic-warning{{use of an empty initializer is a C2x extension}} expected-warning{{excess elements in array initializer}}
   };
   int y4[4][3] = {
     { 1, 3, 5, 2 }, // expected-warning{{excess elements in array initializer}}
@@ -175,7 +175,7 @@ void charArrays(void) {
   char c3[5] = { "Hello" };
   char c4[4] = { "Hello" }; //expected-warning{{initializer-string for char array is too long}}
 
-  int i3[] = {}; //expected-warning{{zero size arrays are an extension}} expected-warning{{use of GNU empty initializer extension}}
+  int i3[] = {}; //expected-warning{{zero size arrays are an extension}} pedantic-warning{{use of an empty initializer is a C2x extension}}
 }
 
 void variableArrayInit(void) {
@@ -197,7 +197,7 @@ const char r7[] = "zxcv";
 char r8[5] = "5char";
 char r9[5] = "6chars"; //expected-warning{{initializer-string for char array is too long}}
 unsigned char r10[] = __extension__ (_Generic(0, int: (__extension__ "foo" )));
-int r11[0] = {}; //expected-warning{{zero size arrays are an extension}} expected-warning{{use of GNU empty initializer extension}}
+int r11[0] = {}; //expected-warning{{zero size arrays are an extension}} pedantic-warning{{use of an empty initializer is a C2x extension}}
 
 // Some struct tests
 void autoStructTest(void) {
@@ -221,8 +221,7 @@ int t8[sizeof t7 == (3*sizeof(int)) ? 1 : -1];
 struct bittest{int : 31, a, :21, :12, b;};
 struct bittest bittestvar = {1, 2, 3, 4}; //expected-warning{{excess elements in struct initializer}}
 
-// Not completely sure what should happen here...
-int u1 = {}; //expected-warning{{use of GNU empty initializer extension}} expected-error{{scalar initializer cannot be empty}}
+int u1 = {}; //pedantic-warning{{use of an empty initializer is a C2x extension}}
 int u2 = {{3}}; //expected-warning{{too many braces around scalar initializer}}
 
 // PR2362

diff  --git a/clang/test/Sema/complex-init-list.c b/clang/test/Sema/complex-init-list.c
index b8f87f57f0793..ac220d8e07c9b 100644
--- a/clang/test/Sema/complex-init-list.c
+++ b/clang/test/Sema/complex-init-list.c
@@ -38,8 +38,7 @@ _Complex float valid5 = {1.0f, 1.0fi}; // expected-warning {{imaginary constants
 struct teststruct invalid1 = { 1, 2 }; // expected-warning {{excess elements}}
 _Complex float invalid2 = { 1, 2, 3 }; // expected-warning {{specifying real and imaginary components is an extension}} \
                                        // expected-warning {{excess elements in scalar initializer}}
-_Complex float invalid3 = {}; // expected-error {{scalar initializer cannot be empty}} \
-                             //  expected-warning {{GNU empty initializer}}
+_Complex float invalid3 = {}; // expected-warning {{use of an empty initializer is a C2x extension}}
 
 
 // Check incomplete array sizing

diff  --git a/clang/test/Sema/compound-literal.c b/clang/test/Sema/compound-literal.c
index 8ed4fd3976d4a..46fa41e55c72c 100644
--- a/clang/test/Sema/compound-literal.c
+++ b/clang/test/Sema/compound-literal.c
@@ -14,7 +14,7 @@ static int *p2 = (int []){2,x}; // expected-error {{initializer element is not a
 static long *p3 = (long []){2,"x"}; // expected-error {{incompatible pointer to integer conversion initializing 'long' with an expression of type 'char[2]'}}
 
 typedef struct { } cache_t; // expected-warning{{empty struct is a GNU extension}}
-static cache_t clo_I1_cache = ((cache_t) { } ); // expected-warning{{use of GNU empty initializer extension}}
+static cache_t clo_I1_cache = ((cache_t) { } ); // expected-warning{{use of an empty initializer is a C2x extension}}
 
 typedef struct Test {int a;int b;} Test;
 static Test* ll = &(Test) {0,0};

diff  --git a/clang/test/Sema/flexible-array-init.c b/clang/test/Sema/flexible-array-init.c
index d3620154c5f0c..57c7344dd33e1 100644
--- a/clang/test/Sema/flexible-array-init.c
+++ b/clang/test/Sema/flexible-array-init.c
@@ -10,7 +10,7 @@ void test(void) {
   struct one x3 = {5, {1, 2, 3}}; // expected-error{{initialization of flexible array member is not allowed}}
   struct one x3a = { 5 };
   struct one x3b = { .a = 5 };
-  struct one x3c = { 5, {} }; // expected-warning{{use of GNU empty initializer extension}} \
+  struct one x3c = { 5, {} }; // expected-warning{{use of an empty initializer is a C2x extension}} \
   // expected-warning{{flexible array initialization is a GNU extension}} \
   // expected-warning{{zero size arrays are an extension}}
 }
@@ -24,13 +24,13 @@ struct bar { struct foo z; }; // expected-warning {{'z' may not be nested in a s
 struct foo a = { 1, { 2, 3, 4 } };        // expected-warning{{flexible array initialization is a GNU extension}}
 struct bar b = { { 1, { 2, 3, 4 } } };    // expected-error{{initialization of flexible array member is not allowed}}
 struct bar c = { { 1, { } } };            // // expected-warning{{flexible array initialization is a GNU extension}} \
-              // expected-warning{{use of GNU empty initializer extension}} \
+              // expected-warning{{use of an empty initializer is a C2x extension}} \
               // expected-warning{{zero size arrays are an extension}}
 struct foo d[1] = { { 1, { 2, 3, 4 } } };  // expected-warning{{'struct foo' may not be used as an array element due to flexible array member}} \
               // expected-error{{initialization of flexible array member is not allowed}}
 
 struct foo desig_foo = { .y = {2, 3, 4} }; // expected-warning{{flexible array initialization is a GNU extension}}
-struct bar desig_bar = { .z.y = { } }; // expected-warning{{use of GNU empty initializer extension}} \
+struct bar desig_bar = { .z.y = { } }; // expected-warning{{use of an empty initializer is a C2x extension}} \
   // expected-warning{{zero size arrays are an extension}} \
   // expected-warning{{flexible array initialization is a GNU extension}}
 struct bar desig_bar2 = { .z.y = { 2, 3, 4} }; // expected-error{{initialization of flexible array member is not allowed}}

diff  --git a/clang/test/Sema/gnu-flags.c b/clang/test/Sema/gnu-flags.c
index 8389cb6055bc4..941d7084869de 100644
--- a/clang/test/Sema/gnu-flags.c
+++ b/clang/test/Sema/gnu-flags.c
@@ -2,13 +2,13 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DALL -Wgnu
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DALL -Wno-gnu \
 // RUN:   -Wgnu-alignof-expression -Wgnu-case-range -Wgnu-complex-integer -Wgnu-conditional-omitted-operand \
-// RUN:   -Wgnu-empty-initializer -Wgnu-label-as-value -Wgnu-statement-expression \
+// RUN:   -Wgnu-label-as-value -Wgnu-statement-expression \
 // RUN:   -Wgnu-compound-literal-initializer -Wgnu-flexible-array-initializer \
 // RUN:   -Wgnu-redeclared-enum  -Wgnu-folding-constant -Wgnu-empty-struct \
 // RUN:   -Wgnu-union-cast -Wgnu-variable-sized-type-not-at-end
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DNONE -Wgnu \
 // RUN:   -Wno-gnu-alignof-expression -Wno-gnu-case-range -Wno-gnu-complex-integer -Wno-gnu-conditional-omitted-operand \
-// RUN:   -Wno-gnu-empty-initializer -Wno-gnu-label-as-value -Wno-gnu-statement-expression \
+// RUN:   -Wno-gnu-label-as-value -Wno-gnu-statement-expression \
 // RUN:   -Wno-gnu-compound-literal-initializer -Wno-gnu-flexible-array-initializer \
 // RUN:   -Wno-gnu-redeclared-enum -Wno-gnu-folding-constant -Wno-gnu-empty-struct \
 // RUN:   -Wno-gnu-union-cast -Wno-gnu-variable-sized-type-not-at-end
@@ -17,7 +17,6 @@
 // %clang_cc1 -fsyntax-only -verify %s -DCASERANGE -Wno-gnu -Wgnu-case-range
 // %clang_cc1 -fsyntax-only -verify %s -DCOMPLEXINT -Wno-gnu -Wgnu-complex-integer
 // %clang_cc1 -fsyntax-only -verify %s -DOMITTEDOPERAND -Wno-gnu -Wgnu-conditional-omitted-operand
-// %clang_cc1 -fsyntax-only -verify %s -DEMPTYINIT -Wno-gnu -Wgnu-empty-initializer
 // %clang_cc1 -fsyntax-only -verify %s -DLABELVALUE -Wno-gnu -Wgnu-label-as-value
 // %clang_cc1 -fsyntax-only -verify %s -DSTATEMENTEXP -Wno-gnu -Wgnu-statement-expression
 // %clang_cc1 -fsyntax-only -verify %s -DSTATEMENTEXPMACRO -Wno-gnu -Wgnu-statement-expression-from-macro-expansion
@@ -67,13 +66,6 @@ _Complex short int complexint;
 static const char* omittedoperand = (const char*)0 ?: "Null";
 
 
-#if ALL || EMPTYINIT
-// expected-warning at +3 {{use of GNU empty initializer extension}}
-#endif
-
-struct { int x; } emptyinit = {};
-
-
 #if ALL || LABELVALUE
 // expected-warning at +6 {{use of GNU address-of-label extension}}
 // expected-warning at +7 {{use of GNU indirect-goto extension}}

diff  --git a/clang/test/Sema/sizeless-1.c b/clang/test/Sema/sizeless-1.c
index 64af214d206b9..8ef30a12f1128 100644
--- a/clang/test/Sema/sizeless-1.c
+++ b/clang/test/Sema/sizeless-1.c
@@ -83,8 +83,8 @@ void func(int sel) {
   svint8_t init_int8 = local_int8;
   svint8_t bad_init_int8 = for; // expected-error {{expected expression}}
 
-  int empty_brace_init_int = {}; // expected-error {{scalar initializer cannot be empty}}
-  svint8_t empty_brace_init_int8 = {}; // expected-error {{initializer for sizeless type 'svint8_t' (aka '__SVInt8_t') cannot be empty}}
+  int empty_brace_init_int = {};
+  svint8_t empty_brace_init_int8 = {};
   svint8_t brace_init_int8 = {local_int8};
   svint8_t bad_brace_init_int8_1 = {local_int8, 0};    // expected-warning {{excess elements in initializer for indivisible sizeless type 'svint8_t'}}
   svint8_t bad_brace_init_int8_2 = {0};                // expected-error {{incompatible type 'int'}}

diff  --git a/clang/test/Sema/vla.c b/clang/test/Sema/vla.c
index c6d61aac4515f..efdc11b85be99 100644
--- a/clang/test/Sema/vla.c
+++ b/clang/test/Sema/vla.c
@@ -113,14 +113,14 @@ void test_fold_to_constant_array(void) {
  jump_over_a2:;
 
   goto jump_over_a3;
-  char a3[ksize] = {}; // expected-warning {{variable length array folded to constant array as an extension}} expected-warning{{use of GNU empty initializer}}
+  char a3[ksize] = {}; // expected-warning {{variable length array folded to constant array as an extension}} expected-warning{{use of an empty initializer is a C2x extension}}
  jump_over_a3:;
 
   goto jump_over_a4; // expected-error{{cannot jump from this goto statement to its label}}
   char a4[ksize][2]; // expected-note{{variable length array}}
  jump_over_a4:;
 
-  char a5[ksize][2] = {}; // expected-warning {{variable length array folded to constant array as an extension}} expected-warning{{use of GNU empty initializer}}
+  char a5[ksize][2] = {}; // expected-warning {{variable length array folded to constant array as an extension}} expected-warning{{use of an empty initializer is a C2x extension}}
 
   int a6[ksize] = {1,2,3,4}; // expected-warning{{variable length array folded to constant array as an extension}}
 

diff  --git a/clang/test/SemaObjC/property.m b/clang/test/SemaObjC/property.m
index b03aea37f92c6..64123f261df59 100644
--- a/clang/test/SemaObjC/property.m
+++ b/clang/test/SemaObjC/property.m
@@ -78,5 +78,5 @@ @interface Test7
 @property unsigned length;
 @end
 void test7(Test7 *t) {
-  char data[t.length] = {}; // expected-error {{variable-sized object may not be initialized}}
+  char data[t.length] = {};
 }

diff  --git a/clang/test/SemaOpenCL/intel-subgroup-avc-ext-types.cl b/clang/test/SemaOpenCL/intel-subgroup-avc-ext-types.cl
index 48ed4c0594d65..adddead3eb412 100644
--- a/clang/test/SemaOpenCL/intel-subgroup-avc-ext-types.cl
+++ b/clang/test/SemaOpenCL/intel-subgroup-avc-ext-types.cl
@@ -28,37 +28,34 @@ void foo(char c, float f, void* v, struct st ss) {
   intel_sub_group_avc_sic_result_t result_sic = ss;
   intel_sub_group_avc_ime_result_single_reference_streamout_t sstreamout = v;
   intel_sub_group_avc_ime_result_dual_reference_streamout_t dstreamin_list = {0x0, 0x1};
-  intel_sub_group_avc_ime_dual_reference_streamin_t dstreamin_list2 = {};
   intel_sub_group_avc_ime_single_reference_streamin_t dstreamin_list3 = {c};
   intel_sub_group_avc_ime_dual_reference_streamin_t dstreamin_list4 = {1};
 #ifdef EXT
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_mce_payload_t' with an expression of incompatible type 'int'}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_ime_payload_t' with an expression of incompatible type 'int'}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_ref_payload_t' with an expression of incompatible type '__private float'}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_sic_payload_t' with an expression of incompatible type '__private struct st'}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_mce_result_t' with an expression of incompatible type 'int'}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_ime_result_t' with an expression of incompatible type 'int'}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_ref_result_t' with an expression of incompatible type '__private float'}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_sic_result_t' with an expression of incompatible type '__private struct st'}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_ime_result_single_reference_streamout_t' with an expression of incompatible type '__private void *__private'}}
-// expected-warning at -14 {{excess elements in struct initializer}}
-// expected-error at -14 {{scalar initializer cannot be empty}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_ime_single_reference_streamin_t' with an expression of incompatible type '__private char'}}
-// expected-error at -14 {{initializing '__private intel_sub_group_avc_ime_dual_reference_streamin_t' with an expression of incompatible type 'int'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_mce_payload_t' with an expression of incompatible type 'int'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_ime_payload_t' with an expression of incompatible type 'int'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_ref_payload_t' with an expression of incompatible type '__private float'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_sic_payload_t' with an expression of incompatible type '__private struct st'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_mce_result_t' with an expression of incompatible type 'int'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_ime_result_t' with an expression of incompatible type 'int'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_ref_result_t' with an expression of incompatible type '__private float'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_sic_result_t' with an expression of incompatible type '__private struct st'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_ime_result_single_reference_streamout_t' with an expression of incompatible type '__private void *__private'}}
+// expected-warning at -13 {{excess elements in struct initializer}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_ime_single_reference_streamin_t' with an expression of incompatible type '__private char'}}
+// expected-error at -13 {{initializing '__private intel_sub_group_avc_ime_dual_reference_streamin_t' with an expression of incompatible type 'int'}}
 #else
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_mce_payload_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_ime_payload_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_ref_payload_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_sic_payload_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_mce_result_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_ime_result_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_ref_result_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_sic_result_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_ime_result_single_reference_streamout_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_ime_result_dual_reference_streamout_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_ime_dual_reference_streamin_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_ime_single_reference_streamin_t'}}
-// expected-error at -28 {{use of undeclared identifier 'intel_sub_group_avc_ime_dual_reference_streamin_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_mce_payload_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_ime_payload_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_ref_payload_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_sic_payload_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_mce_result_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_ime_result_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_ref_result_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_sic_result_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_ime_result_single_reference_streamout_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_ime_result_dual_reference_streamout_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_ime_single_reference_streamin_t'}}
+// expected-error at -26 {{use of undeclared identifier 'intel_sub_group_avc_ime_dual_reference_streamin_t'}}
 #endif
 }
 

diff  --git a/clang/www/c_status.html b/clang/www/c_status.html
index 764c7c58c7a51..a431508e6e16a 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -1083,11 +1083,11 @@ <h2 id="c2x">C2x implementation status</h2>
     </tr>
       <tr> <!-- Feb 2022 -->
         <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2900.htm">N2900</a></td>
-        <td class="unknown" align="center">Unknown</td>
+        <td class="unreleased" align="center">Clang 17</td>
       </tr>
       <tr> <!-- Jul 2022 -->
         <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3011.htm">N3011</a></td>
-        <td class="unknown" align="center">Unknown</td>
+        <td class="unreleased" align="center">Clang 17</td>
       </tr>
     <tr id="typeof">
       <td rowspan="3">Not-so-magic: typeof</td>


        


More information about the cfe-commits mailing list