https://github.com/AaronBallman updated 
https://github.com/llvm/llvm-project/pull/133125

>From 75ef42d644da9136fb07014ade18b6be137426a1 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aa...@aaronballman.com>
Date: Wed, 26 Mar 2025 12:54:29 -0400
Subject: [PATCH 1/3] [C2y] Implement WG14 N3369 and N3469 (_Countof)

C2y adds the _Countof operator which returns the number of elements in
an array. As with sizeof, _Countof either accepts a parenthesized type
name or an expression. Its operand must be (of) an array type. When
passed a constant-size array operand, the operator is a constant
expression which is valid for use as an integer constant expression.

Fixes #102836
---
 clang/docs/LanguageExtensions.rst             |  1 +
 clang/docs/ReleaseNotes.rst                   |  6 ++
 .../clang/Basic/DiagnosticParseKinds.td       |  5 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +
 clang/include/clang/Basic/TokenKinds.def      |  2 +
 clang/lib/AST/ExprConstant.cpp                | 19 +++-
 clang/lib/AST/ItaniumMangle.cpp               |  1 +
 clang/lib/CodeGen/CGExprScalar.cpp            | 15 ++-
 clang/lib/Parse/ParseExpr.cpp                 | 21 ++++-
 clang/lib/Sema/SemaExpr.cpp                   | 39 ++++++--
 clang/test/C/C2y/n3369.c                      | 61 ++++++++++++
 clang/test/C/C2y/n3369_1.c                    | 25 +++++
 clang/test/C/C2y/n3369_2.c                    | 92 +++++++++++++++++++
 clang/test/C/C2y/n3469.c                      | 14 +++
 clang/www/c_status.html                       |  4 +-
 15 files changed, 288 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/C/C2y/n3369.c
 create mode 100644 clang/test/C/C2y/n3369_1.c
 create mode 100644 clang/test/C/C2y/n3369_2.c
 create mode 100644 clang/test/C/C2y/n3469.c

diff --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index d4771775c9739..8b5707ce2acac 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1653,6 +1653,7 @@ Array & element qualification (N2607)                     
                     C
 Attributes (N2335)                                                             
C23           C89
 ``#embed`` (N3017)                                                             
C23           C89, C++
 Octal literals prefixed with ``0o`` or ``0O``                                  
C2y           C89, C++
+``_Countof`` (N3369, N3469)                                                    
C2y           C89
 ============================================= ================================ 
============= =============
 
 Builtin type aliases
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 04ec2cfef679c..b82e79c092c4e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -141,6 +141,12 @@ C2y Feature Support
   paper also introduced octal and hexadecimal delimited escape sequences (e.g.,
   ``"\x{12}\o{12}"``) which are also supported as an extension in older C
   language modes.
+- Implemented `WG14 N3369 
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3369.pdf>`_
+  which introduces the ``_Lengthof`` operator, and `WG14 N3469 
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3469.htm>`_
+  which renamed ``_Lengthof`` to ``_Countof``. This feature is implemented as
+  a conforming extension in earlier C language modes, but not in C++ language
+  modes (``std::extent`` and ``std::rank`` already provide the same
+  functionality but with more granularity).
 
 C23 Feature Support
 ^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 4dc956f7ae6f7..86c361b4dbcf7 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -171,12 +171,17 @@ def ext_c99_feature : Extension<
   "'%0' is a C99 extension">, InGroup<C99>;
 def ext_c11_feature : Extension<
   "'%0' is a C11 extension">, InGroup<C11>;
+def ext_c2y_feature : Extension<
+  "'%0' is a C2y extension">, InGroup<C2y>;
 def warn_c11_compat_keyword : Warning<
   "'%0' is incompatible with C standards before C11">,
   InGroup<CPre11Compat>, DefaultIgnore;
 def warn_c23_compat_keyword : Warning<
  "'%0' is incompatible with C standards before C23">,
  InGroup<CPre23Compat>, DefaultIgnore;
+def warn_c2y_compat_keyword : Warning<
+  "'%0' is incompatible with C standards before C2y">,
+  InGroup<CPre2yCompat>, DefaultIgnore;
 
 def err_c11_noreturn_misplaced : Error<
   "'_Noreturn' keyword must precede function declarator">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c77cde297dc32..1e900437d41ce 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7022,6 +7022,8 @@ def err_sizeof_alignof_typeof_bitfield : Error<
   "bit-field">;
 def err_alignof_member_of_incomplete_type : Error<
   "invalid application of 'alignof' to a field of a class still being 
defined">;
+def err_countof_arg_not_array_type : Error<
+  "'_Countof' requires an argument of array type; %0 invalid">;
 def err_vecstep_non_scalar_vector_type : Error<
   "'vec_step' requires built-in scalar or vector type, %0 invalid">;
 def err_offsetof_incomplete_type : Error<
diff --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index 1bf9f43f80986..880928ae0447d 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -349,6 +349,8 @@ KEYWORD(__func__                    , KEYALL)
 KEYWORD(__objc_yes                  , KEYALL)
 KEYWORD(__objc_no                   , KEYALL)
 
+// C2y
+UNARY_EXPR_OR_TYPE_TRAIT(_Countof, CountOf, KEYNOCXX)
 
 // C++ 2.11p1: Keywords.
 KEYWORD(asm                         , KEYCXX|KEYGNU)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 95da7b067b459..92b1f41bf2fab 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -14926,6 +14926,23 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
 
     return false;
   }
+  case UETT_CountOf: {
+    QualType Ty = E->getTypeOfArgument();
+    assert(Ty->isArrayType());
+
+    // We don't need to worry about array element qualifiers, so getting the
+    // unsafe array type is fine.
+    if (const auto *CAT =
+            dyn_cast<ConstantArrayType>(Ty->getAsArrayTypeUnsafe())) {
+      return Success(CAT->getSize(), E);
+    }
+
+    // If it wasn't a constant array, it's not a valid constant expression.
+    assert(!Ty->isConstantSizeType());
+    // FIXME: Better diagnostic.
+    Info.FFDiag(E->getBeginLoc());
+    return false;
+  }
   }
 
   llvm_unreachable("unknown expr/type trait");
@@ -17425,7 +17442,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext 
&Ctx) {
   }
   case Expr::UnaryExprOrTypeTraitExprClass: {
     const UnaryExprOrTypeTraitExpr *Exp = cast<UnaryExprOrTypeTraitExpr>(E);
-    if ((Exp->getKind() ==  UETT_SizeOf) &&
+    if ((Exp->getKind() ==  UETT_SizeOf || Exp->getKind() == UETT_CountOf) &&
         Exp->getTypeOfArgument()->isVariableArrayType())
       return ICEDiag(IK_NotICE, E->getBeginLoc());
     return NoDiag();
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 917402544d4f6..981cdb3c806b1 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5367,6 +5367,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, 
unsigned Arity,
       MangleAlignofSizeofArg();
       break;
 
+    case UETT_CountOf:
     case UETT_VectorElements:
     case UETT_OpenMPRequiredSimdAlign:
     case UETT_VecStep:
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index eccdcdb497f84..e858de7a4f6d2 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -3477,7 +3477,7 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
                               const UnaryExprOrTypeTraitExpr *E) {
   QualType TypeToSize = E->getTypeOfArgument();
   if (auto Kind = E->getKind();
-      Kind == UETT_SizeOf || Kind == UETT_DataSizeOf) {
+      Kind == UETT_SizeOf || Kind == UETT_DataSizeOf || Kind == UETT_CountOf) {
     if (const VariableArrayType *VAT =
             CGF.getContext().getAsVariableArrayType(TypeToSize)) {
       if (E->isArgumentType()) {
@@ -3492,10 +3492,15 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
       auto VlaSize = CGF.getVLASize(VAT);
       llvm::Value *size = VlaSize.NumElts;
 
-      // Scale the number of non-VLA elements by the non-VLA element size.
-      CharUnits eltSize = CGF.getContext().getTypeSizeInChars(VlaSize.Type);
-      if (!eltSize.isOne())
-        size = CGF.Builder.CreateNUWMul(CGF.CGM.getSize(eltSize), size);
+      // For sizeof and __datasizeof, we need to scale the number of elements
+      // by the size of the array element type. For _Countof, we just want to
+      // return the size directly.
+      if (Kind != UETT_CountOf) {
+        // Scale the number of non-VLA elements by the non-VLA element size.
+        CharUnits eltSize = CGF.getContext().getTypeSizeInChars(VlaSize.Type);
+        if (!eltSize.isOne())
+          size = CGF.Builder.CreateNUWMul(CGF.CGM.getSize(eltSize), size);
+      }
 
       return size;
     }
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 0c28972d6ed8f..0a22f7372a9f9 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -904,6 +904,8 @@ ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
 /// [GNU]   '__alignof' '(' type-name ')'
 /// [C11]   '_Alignof' '(' type-name ')'
 /// [C++11] 'alignof' '(' type-id ')'
+/// [C2y]   '_Countof' unary-expression
+/// [C2y]   '_Countof' '(' type-name ')'
 /// [GNU]   '&&' identifier
 /// [C++11] 'noexcept' '(' expression ')' [C++11 5.3.7]
 /// [C++]   new-expression
@@ -1544,6 +1546,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind 
ParseKind,
   // unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')'
   case tok::kw___builtin_omp_required_simd_align:
   case tok::kw___builtin_vectorelements:
+  case tok::kw__Countof:
     if (NotPrimaryExpression)
       *NotPrimaryExpression = true;
     AllowSuffix = false;
@@ -2463,7 +2466,7 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token 
&OpTok,
                        tok::kw___datasizeof, tok::kw___alignof, 
tok::kw_alignof,
                        tok::kw__Alignof, tok::kw_vec_step,
                        tok::kw___builtin_omp_required_simd_align,
-                       tok::kw___builtin_vectorelements) &&
+                       tok::kw___builtin_vectorelements, tok::kw__Countof) &&
          "Not a typeof/sizeof/alignof/vec_step expression!");
 
   ExprResult Operand;
@@ -2510,9 +2513,9 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token 
&OpTok,
     // is not going to help when the nesting is too deep. In this corner case
     // we continue to parse with sufficient stack space to avoid crashing.
     if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof,
-                      tok::kw_alignof, tok::kw__Alignof) &&
+                      tok::kw_alignof, tok::kw__Alignof, tok::kw__Countof) &&
         Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof,
-                    tok::kw_alignof, tok::kw__Alignof))
+                    tok::kw_alignof, tok::kw__Alignof, tok::kw__Countof))
       Actions.runWithSufficientStackSpace(Tok.getLocation(), [&] {
         Operand = ParseCastExpression(UnaryExprOnly);
       });
@@ -2594,12 +2597,14 @@ ExprResult 
Parser::ParseSYCLUniqueStableNameExpression() {
 /// [GNU]   '__alignof' '(' type-name ')'
 /// [C11]   '_Alignof' '(' type-name ')'
 /// [C++11] 'alignof' '(' type-id ')'
+/// [C2y]   '_Countof' unary-expression
+/// [C2y]   '_Countof' '(' type-name ')'
 /// \endverbatim
 ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
   assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof,
                      tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step,
                      tok::kw___builtin_omp_required_simd_align,
-                     tok::kw___builtin_vectorelements) &&
+                     tok::kw___builtin_vectorelements, tok::kw__Countof) &&
          "Not a sizeof/alignof/vec_step expression!");
   Token OpTok = Tok;
   ConsumeToken();
@@ -2656,6 +2661,8 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
     Diag(OpTok, diag::warn_cxx98_compat_alignof);
   else if (getLangOpts().C23 && OpTok.is(tok::kw_alignof))
     Diag(OpTok, diag::warn_c23_compat_keyword) << OpTok.getName();
+  else if (getLangOpts().C2y && OpTok.is(tok::kw__Countof))
+    Diag(OpTok, diag::warn_c2y_compat_keyword) << OpTok.getName();
 
   EnterExpressionEvaluationContext Unevaluated(
       Actions, Sema::ExpressionEvaluationContext::Unevaluated,
@@ -2690,6 +2697,12 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() 
{
   case tok::kw___builtin_vectorelements:
     ExprKind = UETT_VectorElements;
     break;
+  case tok::kw__Countof:
+    ExprKind = UETT_CountOf;
+    assert(!getLangOpts().CPlusPlus && "_Countof in C++ mode?");
+    if (!getLangOpts().C2y)
+      Diag(OpTok, diag::ext_c2y_feature) << OpTok.getName();
+    break;
   default:
     break;
   }
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3af6d6c23438f..f7554d90ee4c5 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4266,7 +4266,7 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E,
   bool IsUnevaluatedOperand =
       (ExprKind == UETT_SizeOf || ExprKind == UETT_DataSizeOf ||
        ExprKind == UETT_AlignOf || ExprKind == UETT_PreferredAlignOf ||
-       ExprKind == UETT_VecStep);
+       ExprKind == UETT_VecStep || ExprKind == UETT_CountOf);
   if (IsUnevaluatedOperand) {
     ExprResult Result = CheckUnevaluatedOperand(E);
     if (Result.isInvalid())
@@ -4338,6 +4338,21 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E,
                                        E->getSourceRange(), ExprKind))
     return true;
 
+  if (ExprKind == UETT_CountOf) {
+    // The type has to be an array type. We already checked for incomplete
+    // types above.
+    QualType ExprType = E->IgnoreParens()->getType();
+    if (!ExprType->isArrayType()) {
+      Diag(E->getExprLoc(), diag::err_countof_arg_not_array_type) << ExprType;
+      return true;
+    }
+    // FIXME: warn on _Countof on an array parameter. Not warning on it
+    // currently because there are papers in WG14 about array types which do
+    // not decay that could impact this behavior, so we want to see if anything
+    // changes here before coming up with a warning group for _Countof-related
+    // diagnostics.
+  }
+
   if (ExprKind == UETT_SizeOf) {
     if (const auto *DeclRef = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
       if (const auto *PVD = dyn_cast<ParmVarDecl>(DeclRef->getFoundDecl())) {
@@ -4608,6 +4623,15 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType 
ExprType,
     return true;
   }
 
+  if (ExprKind == UETT_CountOf) {
+    // The type has to be an array type. We already checked for incomplete
+    // types above.
+    if (!ExprType->isArrayType()) {
+      Diag(OpLoc, diag::err_countof_arg_not_array_type) << ExprType;
+      return true;
+    }
+  }
+
   // WebAssembly tables are always illegal operands to unary expressions and
   // type traits.
   if (Context.getTargetInfo().getTriple().isWasm() &&
@@ -4666,7 +4690,8 @@ ExprResult 
Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo,
   // properly deal with VLAs in nested calls of sizeof and typeof.
   if (currentEvaluationContext().isUnevaluated() &&
       currentEvaluationContext().InConditionallyConstantEvaluateContext &&
-      ExprKind == UETT_SizeOf && TInfo->getType()->isVariablyModifiedType())
+      (ExprKind == UETT_SizeOf || ExprKind == UETT_CountOf) &&
+      TInfo->getType()->isVariablyModifiedType())
     TInfo = TransformToPotentiallyEvaluated(TInfo);
 
   // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t.
@@ -4697,16 +4722,16 @@ Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, 
SourceLocation OpLoc,
   } else if (E->refersToBitField()) {  // C99 6.5.3.4p1.
     Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 0;
     isInvalid = true;
-  } else if (ExprKind == UETT_VectorElements) {
-    isInvalid = CheckUnaryExprOrTypeTraitOperand(E, UETT_VectorElements);
-  } else {
-    isInvalid = CheckUnaryExprOrTypeTraitOperand(E, UETT_SizeOf);
+  } else if (ExprKind == UETT_VectorElements || ExprKind == UETT_SizeOf ||
+             ExprKind == UETT_CountOf) { // FIXME: __datasizeof?
+    isInvalid = CheckUnaryExprOrTypeTraitOperand(E, ExprKind);
   }
 
   if (isInvalid)
     return ExprError();
 
-  if (ExprKind == UETT_SizeOf && E->getType()->isVariableArrayType()) {
+  if ((ExprKind == UETT_SizeOf || ExprKind == UETT_CountOf) &&
+      E->getType()->isVariableArrayType()) {
     PE = TransformToPotentiallyEvaluated(E);
     if (PE.isInvalid()) return ExprError();
     E = PE.get();
diff --git a/clang/test/C/C2y/n3369.c b/clang/test/C/C2y/n3369.c
new file mode 100644
index 0000000000000..c199feb7f9d54
--- /dev/null
+++ b/clang/test/C/C2y/n3369.c
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c2y -pedantic -Wall -Wno-comment -verify 
%s
+
+/* WG14 N3369: Clang 21
+ * _Lengthof operator
+ *
+ * Adds an operator to get the length of an array. Note that WG14 N3469 renamed
+ * this operator to _Countof.
+ */
+
+int global_array[12];
+
+void test_parsing_failures() {
+  (void)_Countof;     // expected-error {{expected expression}}
+  (void)_Countof(;    // expected-error {{expected expression}}
+  (void)_Countof();   // expected-error {{expected expression}}
+  (void)_Countof int; // expected-error {{expected expression}}
+}
+
+void test_semantic_failures() {
+  (void)_Countof(1);         // expected-error {{'_Countof' requires an 
argument of array type; 'int' invalid}}
+  int non_array;
+  (void)_Countof non_array;  // expected-error {{'_Countof' requires an 
argument of array type; 'int' invalid}}  
+  (void)_Countof(int);       // expected-error {{'_Countof' requires an 
argument of array type; 'int' invalid}}  
+}
+
+void test_constant_expression_behavior(int n) {
+  static_assert(_Countof(global_array) == 12);
+  static_assert(_Countof global_array == 12);  
+  static_assert(_Countof(int[12]) == 12);
+
+  // Use of a VLA makes it not a constant expression, same as with sizeof.
+  int array[n];
+  static_assert(_Countof(array)); // expected-error {{static assertion 
expression is not an integral constant expression}}
+  static_assert(sizeof(array));   // expected-error {{static assertion 
expression is not an integral constant expression}}
+  static_assert(_Countof(int[n]));// expected-error {{static assertion 
expression is not an integral constant expression}}
+  static_assert(sizeof(int[n]));  // expected-error {{static assertion 
expression is not an integral constant expression}}
+  
+  // Constant folding works the same way as sizeof, too.
+  const int m = 12;
+  int other_array[m];
+  static_assert(sizeof(other_array));   // expected-error {{static assertion 
expression is not an integral constant expression}}
+  static_assert(_Countof(other_array)); // expected-error {{static assertion 
expression is not an integral constant expression}}
+  static_assert(sizeof(int[m]));        // expected-error {{static assertion 
expression is not an integral constant expression}}
+  static_assert(_Countof(int[m]));      // expected-error {{static assertion 
expression is not an integral constant expression}}
+  
+  // Note that this applies to each array dimension.
+  int another_array[n][7];
+  static_assert(_Countof(another_array)); // expected-error {{static assertion 
expression is not an integral constant expression}}
+  static_assert(_Countof(*another_array) == 7);
+}
+
+void test_with_function_param(int array[12], int (*array_ptr)[12]) {
+  (void)_Countof(array); // expected-error {{'_Countof' requires an argument 
of array type; 'int *' invalid}}
+  static_assert(_Countof(*array_ptr) == 12);
+}
+
+void test_multidimensional_arrays() {
+  int array[12][7];
+  static_assert(_Countof(array) == 12);
+  static_assert(_Countof(*array) == 7);
+}
diff --git a/clang/test/C/C2y/n3369_1.c b/clang/test/C/C2y/n3369_1.c
new file mode 100644
index 0000000000000..b4e75151a5404
--- /dev/null
+++ b/clang/test/C/C2y/n3369_1.c
@@ -0,0 +1,25 @@
+/* RUN: %clang_cc1 -fsyntax-only -std=c2y -pedantic -Wpre-c2y-compat 
-verify=compat %s
+   RUN: %clang_cc1 -fsyntax-only -std=c23 -pedantic -verify %s
+   RUN: %clang_cc1 -fsyntax-only -std=c89 -pedantic 
-verify=expected,static-assert %s
+   RUN: %clang_cc1 -fsyntax-only -pedantic -verify=cpp,static-assert -x c++ %s
+ */
+
+/* This tests the extension behavior for _Countof in language modes before C2y.
+ * It also tests the behavior of the precompat warning. And it tests the
+ * behavior in C++ mode where the extension is not supported.
+ */
+int array[12];
+int x = _Countof(array);   /* expected-warning {{'_Countof' is a C2y 
extension}}
+                              compat-warning {{'_Countof' is incompatible with 
C standards before C2y}}
+                              cpp-error {{use of undeclared identifier 
'_Countof'}}
+                            */
+int y = _Countof(int[12]); /* expected-warning {{'_Countof' is a C2y 
extension}}
+                              compat-warning {{'_Countof' is incompatible with 
C standards before C2y}}
+                              cpp-error {{expected '(' for function-style cast 
or type construction}}
+                            */
+
+_Static_assert(_Countof(int[12]) == 12, ""); /* expected-warning {{'_Countof' 
is a C2y extension}}
+                                                compat-warning {{'_Countof' is 
incompatible with C standards before C2y}}
+                                                cpp-error {{expected '(' for 
function-style cast or type construction}}
+                                                static-assert-warning 
{{'_Static_assert' is a C11 extension}}
+                                              */
diff --git a/clang/test/C/C2y/n3369_2.c b/clang/test/C/C2y/n3369_2.c
new file mode 100644
index 0000000000000..3f324040c500e
--- /dev/null
+++ b/clang/test/C/C2y/n3369_2.c
@@ -0,0 +1,92 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 5
+// RUN: %clang_cc1 -std=c2y -emit-llvm -o - %s | FileCheck %s
+
+// This tests the codegen behavior for _Countof.
+// CHECK-LABEL: define dso_local i32 @test1(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARRAY:%.*]] = alloca [12 x i32], align 16
+// CHECK-NEXT:    ret i32 12
+//
+int test1() {
+  int array[12];
+  return _Countof(array);
+}
+
+// CHECK-LABEL: define dso_local i32 @test2(
+// CHECK-SAME: i32 noundef [[N:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[N_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[SAVED_STACK:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[__VLA_EXPR0:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i32 [[N]], ptr [[N_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[N_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = zext i32 [[TMP0]] to i64
+// CHECK-NEXT:    [[TMP2:%.*]] = call ptr @llvm.stacksave.p0()
+// CHECK-NEXT:    store ptr [[TMP2]], ptr [[SAVED_STACK]], align 8
+// CHECK-NEXT:    [[VLA:%.*]] = alloca i32, i64 [[TMP1]], align 16
+// CHECK-NEXT:    store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8
+// CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP1]] to i32
+// CHECK-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8
+// CHECK-NEXT:    call void @llvm.stackrestore.p0(ptr [[TMP3]])
+// CHECK-NEXT:    ret i32 [[CONV]]
+//
+int test2(int n) {
+  int array[n];
+  return _Countof(array);
+}
+
+// CHECK-LABEL: define dso_local i32 @test3(
+// CHECK-SAME: i32 noundef [[N:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[N_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[N]], ptr [[N_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[N_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = zext i32 [[TMP0]] to i64
+// CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP1]] to i32
+// CHECK-NEXT:    ret i32 [[CONV]]
+//
+int test3(int n) {
+  return _Countof(int[n]);
+}
+
+// CHECK-LABEL: define dso_local i32 @test4(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    ret i32 100
+//
+int test4() {
+  return _Countof(float[100]);
+}
+
+// CHECK-LABEL: define dso_local i32 @test5(
+// CHECK-SAME: i32 noundef [[N:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[N_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[SAVED_STACK:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[__VLA_EXPR0:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[X:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[Y:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[N]], ptr [[N_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[N_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = zext i32 [[TMP0]] to i64
+// CHECK-NEXT:    [[TMP2:%.*]] = call ptr @llvm.stacksave.p0()
+// CHECK-NEXT:    store ptr [[TMP2]], ptr [[SAVED_STACK]], align 8
+// CHECK-NEXT:    [[VLA:%.*]] = alloca [7 x i32], i64 [[TMP1]], align 16
+// CHECK-NEXT:    store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8
+// CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP1]] to i32
+// CHECK-NEXT:    store i32 [[CONV]], ptr [[X]], align 4
+// CHECK-NEXT:    store i32 7, ptr [[Y]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr [[X]], align 4
+// CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[TMP3]], [[TMP4]]
+// CHECK-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8
+// CHECK-NEXT:    call void @llvm.stackrestore.p0(ptr [[TMP5]])
+// CHECK-NEXT:    ret i32 [[ADD]]
+//
+int test5(int n) {
+  int array[n][7];
+  int x = _Countof(array);
+  int y = _Countof(*array);
+  return x + y;
+}
diff --git a/clang/test/C/C2y/n3469.c b/clang/test/C/C2y/n3469.c
new file mode 100644
index 0000000000000..3d9ac8e6411e9
--- /dev/null
+++ b/clang/test/C/C2y/n3469.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c2y -verify %s
+
+/* WG14 N3469: Clang 21
+ * The Big Array Size Survey
+ *
+ * This renames _Lengthof to _Countof.
+ */
+
+void test() {
+  (void)_Countof(int[12]); // Ok
+  (void)_Lengthof(int[12]); // expected-error {{use of undeclared identifier 
'_Lengthof'}} \
+                               expected-error {{expected expression}}
+}
+
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index c9e2eda4304f3..77bb0ca165c72 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -243,11 +243,11 @@ <h2 id="c2y">C2y implementation status</h2>
     </tr>
       <tr>
         <td><a 
href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3369.pdf";>N3369</a></td>
-        <td class="none" align="center">No</td>
+        <td class="unreleased" align="center">Clang 21</td>
       </tr>
       <tr> <!-- Graz Feb 2025 -->
         <td><a 
href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3469.htm";>N3469</a></td>
-        <td class="none" align="center">No</td>
+        <td class="unreleased" align="center">Clang 21</td>
       </tr>
     <tr>
       <td>Named loops, v3</td>

>From 12db58572c15ad4c2a9deeb4896dec03a8c21a60 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aa...@aaronballman.com>
Date: Wed, 26 Mar 2025 13:51:57 -0400
Subject: [PATCH 2/3] Fix formatting; NFC

---
 clang/lib/AST/ExprConstant.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 92b1f41bf2fab..31589ec553e56 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17442,7 +17442,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext 
&Ctx) {
   }
   case Expr::UnaryExprOrTypeTraitExprClass: {
     const UnaryExprOrTypeTraitExpr *Exp = cast<UnaryExprOrTypeTraitExpr>(E);
-    if ((Exp->getKind() ==  UETT_SizeOf || Exp->getKind() == UETT_CountOf) &&
+    if ((Exp->getKind() == UETT_SizeOf || Exp->getKind() == UETT_CountOf) &&
         Exp->getTypeOfArgument()->isVariableArrayType())
       return ICEDiag(IK_NotICE, E->getBeginLoc());
     return NoDiag();

>From 301b038845c9f3f02f750210a8467e78106cb314 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aa...@aaronballman.com>
Date: Wed, 26 Mar 2025 13:53:50 -0400
Subject: [PATCH 3/3] Fix bit-field overflow caught by tests

---
 clang/include/clang/AST/Stmt.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index fa4abdb489203..1ab951a005fd9 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -531,7 +531,7 @@ class alignas(void *) Stmt {
     unsigned : NumExprBits;
 
     LLVM_PREFERRED_TYPE(UnaryExprOrTypeTrait)
-    unsigned Kind : 3;
+    unsigned Kind : 4;
     LLVM_PREFERRED_TYPE(bool)
     unsigned IsType : 1; // true if operand is a type, false if an expression.
   };

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to