[clang] [C2y] Support WG14 N3457, the __COUNTER__ macro (PR #162662)

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 9 09:17:56 PDT 2025


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

>From 7a5e159aceef541c054a53f6508cdb7fdd9af04d Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Thu, 9 Oct 2025 10:12:55 -0400
Subject: [PATCH 1/5] [C2y] Support WG14 N3457, the __COUNTER__ macro

This implements the parts of https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm
which were adopted at the recent meeting in Brno.

Clang already implemented __COUNTER__, but needed some changes for
conformance. Specifically, we now diagnose when the macro is expanded
more than 2147483647 times. Additionally, we now give the expected
extension and pre-compat warnings for the feature.

To support testing the limits, this also adds a -cc1-only option,
-finitial-counter-value=, which lets you specify the initial value the
__COUNTER__ macro should expand to.
---
 clang/docs/LanguageExtensions.rst             |  4 +-
 clang/docs/ReleaseNotes.rst                   |  5 +++
 .../include/clang/Basic/DiagnosticLexKinds.td |  8 ++++
 clang/include/clang/Driver/Options.td         |  4 ++
 clang/include/clang/Lex/Preprocessor.h        |  6 +--
 clang/include/clang/Lex/PreprocessorOptions.h |  4 ++
 clang/include/clang/Serialization/ASTReader.h |  8 ++--
 clang/lib/Frontend/ASTUnit.cpp                |  9 +++--
 clang/lib/Frontend/InitPreprocessor.cpp       |  3 ++
 clang/lib/Lex/PPMacroExpansion.cpp            | 13 ++++++-
 clang/lib/Serialization/ASTReader.cpp         |  4 +-
 clang/test/C/C2y/n3457.c                      | 37 +++++++++++++++++++
 clang/test/C/C2y/n3457_1.c                    | 20 ++++++++++
 clang/test/C/C2y/n3457_2.c                    | 10 +++++
 clang/www/c_status.html                       |  2 +-
 15 files changed, 122 insertions(+), 15 deletions(-)
 create mode 100644 clang/test/C/C2y/n3457.c
 create mode 100644 clang/test/C/C2y/n3457_1.c
 create mode 100644 clang/test/C/C2y/n3457_2.c

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 6bb99c757cd19..54c215e9ccfaa 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -385,7 +385,9 @@ Builtin Macros
 
 ``__COUNTER__``
   Defined to an integer value that starts at zero and is incremented each time
-  the ``__COUNTER__`` macro is expanded.
+  the ``__COUNTER__`` macro is expanded. This is a standard feature in C2y but
+  is an extension in earlier language modes and in C++. This macro can only be
+  expanded 2147483647 times at most.
 
 ``__INCLUDE_LEVEL__``
   Defined to an integral value that is the include depth of the file currently
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5e9a71e1e74d6..37e02e9638296 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -181,6 +181,11 @@ C Language Changes
 C2y Feature Support
 ^^^^^^^^^^^^^^^^^^^
 - Clang now supports `N3355 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3355.htm>`_ Named Loops.
+- Clang's implementation of ``__COUNTER__`` was updated to conform to
+  `WG14 N3457 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm>`_.
+  This includes adding pedantic warnings for the feature being an extension in
+  other language modes as well as an error when the counter is expanded more
+  than 2147483647 times.
 
 C23 Feature Support
 ^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index c7fe6e1db6d1f..46a7a88f7a50b 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -90,6 +90,14 @@ def err_unterminated___pragma : Error<"missing terminating ')' character">;
 
 def err_conflict_marker : Error<"version control conflict marker in file">;
 
+def err_counter_overflow : Error<
+  "'__COUNTER__' value cannot exceed 2147483647">;
+def ext_counter : Extension<
+  "'__COUNTER__' is a C2y extension">, InGroup<C2y>;
+def warn_counter : Warning<
+  "'__COUNTER__' is incompatible with standards before C2y">,
+  InGroup<CPre2yCompat>, DefaultIgnore;
+
 def err_raw_delim_too_long : Error<
   "raw string delimiter longer than 16 characters"
   "; use PREFIX( )PREFIX to delimit raw string">;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index c8e96e125733c..2511f88f4e53c 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -8417,6 +8417,10 @@ def aligned_alloc_unavailable : Flag<["-"], "faligned-alloc-unavailable">,
   MarshallingInfoFlag<LangOpts<"AlignedAllocationUnavailable">>,
   ShouldParseIf<faligned_allocation.KeyPath>;
 
+def finitial_counter_value_EQ : Joined<["-"], "finitial-counter-value=">,
+  HelpText<"Sets the initial value for __COUNTER__, defaults to 0.">,
+  MarshallingInfoInt<PreprocessorOpts<"InitialCounterValue">, "0">;
+
 } // let Visibility = [CC1Option]
 
 //===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 39754847a93e4..b79e6072cff15 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -226,7 +226,7 @@ class Preprocessor {
       LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine;
 
   // Next __COUNTER__ value, starts at 0.
-  unsigned CounterValue = 0;
+  unsigned long CounterValue = 0;
 
   enum {
     /// Maximum depth of \#includes.
@@ -2421,8 +2421,8 @@ class Preprocessor {
   bool SawDateOrTime() const {
     return DATELoc != SourceLocation() || TIMELoc != SourceLocation();
   }
-  unsigned getCounterValue() const { return CounterValue; }
-  void setCounterValue(unsigned V) { CounterValue = V; }
+  unsigned long getCounterValue() const { return CounterValue; }
+  void setCounterValue(unsigned long V) { CounterValue = V; }
 
   LangOptions::FPEvalMethodKind getCurrentFPEvalMethod() const {
     assert(CurrentFPEvalMethod != LangOptions::FEM_UnsetOnCommandLine &&
diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h
index d4c4e1ccbf2c4..2b65e9422a5e5 100644
--- a/clang/include/clang/Lex/PreprocessorOptions.h
+++ b/clang/include/clang/Lex/PreprocessorOptions.h
@@ -198,6 +198,10 @@ class PreprocessorOptions {
   /// If set, the UNIX timestamp specified by SOURCE_DATE_EPOCH.
   std::optional<uint64_t> SourceDateEpoch;
 
+  /// The initial value for __COUNTER__; typically is zero but can be set via a
+  /// -cc1 flag for testing purposes.
+  unsigned long InitialCounterValue = 0;
+
 public:
   PreprocessorOptions() : PrecompiledPreambleBytes(0, false) {}
 
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index af856a8097ab1..485bfad8c9e1a 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -221,7 +221,7 @@ class ASTReaderListener {
 
   /// Receives __COUNTER__ value.
   virtual void ReadCounter(const serialization::ModuleFile &M,
-                           unsigned Value) {}
+                           unsigned long Value) {}
 
   /// This is called for each AST file loaded.
   virtual void visitModuleFile(StringRef Filename,
@@ -312,7 +312,8 @@ class ChainedASTReaderListener : public ASTReaderListener {
                                bool Complain,
                                std::string &SuggestedPredefines) override;
 
-  void ReadCounter(const serialization::ModuleFile &M, unsigned Value) override;
+  void ReadCounter(const serialization::ModuleFile &M,
+                   unsigned long Value) override;
   bool needsInputFileVisitation() override;
   bool needsSystemInputFileVisitation() override;
   void visitModuleFile(StringRef Filename,
@@ -352,7 +353,8 @@ class PCHValidator : public ASTReaderListener {
                                StringRef ModuleFilename,
                                StringRef SpecificModuleCachePath,
                                bool Complain) override;
-  void ReadCounter(const serialization::ModuleFile &M, unsigned Value) override;
+  void ReadCounter(const serialization::ModuleFile &M,
+                   unsigned long Value) override;
 };
 
 /// ASTReaderListenter implementation to set SuggestedPredefines of
diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp
index cb445682ac48b..ff48faa02f593 100644
--- a/clang/lib/Frontend/ASTUnit.cpp
+++ b/clang/lib/Frontend/ASTUnit.cpp
@@ -520,7 +520,7 @@ class ASTInfoCollector : public ASTReaderListener {
   CodeGenOptions &CodeGenOpts;
   std::shared_ptr<TargetOptions> &TargetOpts;
   IntrusiveRefCntPtr<TargetInfo> &Target;
-  unsigned &Counter;
+  unsigned long &Counter;
   bool InitializedLanguage = false;
   bool InitializedHeaderSearchPaths = false;
 
@@ -529,7 +529,8 @@ class ASTInfoCollector : public ASTReaderListener {
                    HeaderSearchOptions &HSOpts, PreprocessorOptions &PPOpts,
                    LangOptions &LangOpt, CodeGenOptions &CodeGenOpts,
                    std::shared_ptr<TargetOptions> &TargetOpts,
-                   IntrusiveRefCntPtr<TargetInfo> &Target, unsigned &Counter)
+                   IntrusiveRefCntPtr<TargetInfo> &Target,
+                   unsigned long &Counter)
       : PP(PP), Context(Context), HSOpts(HSOpts), PPOpts(PPOpts),
         LangOpt(LangOpt), CodeGenOpts(CodeGenOpts), TargetOpts(TargetOpts),
         Target(Target), Counter(Counter) {}
@@ -627,7 +628,7 @@ class ASTInfoCollector : public ASTReaderListener {
   }
 
   void ReadCounter(const serialization::ModuleFile &M,
-                   unsigned Value) override {
+                   unsigned long Value) override {
     Counter = Value;
   }
 
@@ -873,7 +874,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
       /*isysroot=*/"",
       /*DisableValidationKind=*/disableValid, AllowASTWithCompilerErrors);
 
-  unsigned Counter = 0;
+  unsigned long Counter = 0;
   AST->Reader->setListener(std::make_unique<ASTInfoCollector>(
       *AST->PP, AST->Ctx.get(), *AST->HSOpts, *AST->PPOpts, *AST->LangOpts,
       *AST->CodeGenOpts, AST->TargetOpts, AST->Target, Counter));
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 877ab02850667..aaf01705caa5e 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1569,6 +1569,9 @@ void clang::InitializePreprocessor(Preprocessor &PP,
   llvm::raw_string_ostream Predefines(PredefineBuffer);
   MacroBuilder Builder(Predefines);
 
+  // Ensure that the initial value of __COUNTER__ is hooked up.
+  PP.setCounterValue(InitOpts.InitialCounterValue);
+
   // Emit line markers for various builtin sections of the file. The 3 here
   // marks <built-in> as being a system header, which suppresses warnings when
   // the same macro is defined multiple times.
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index dec1956ea0f9a..37eef9060d4b5 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1737,7 +1737,18 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
       Diag(getLastFPEvalPragmaLocation(), diag::note_pragma_entered_here);
     }
   } else if (II == Ident__COUNTER__) {
-    // __COUNTER__ expands to a simple numeric value.
+    Diag(Tok.getLocation(),
+         getLangOpts().C2y ? diag::warn_counter : diag::ext_counter);
+    // __COUNTER__ expands to a simple numeric value that must be less than
+    // 2147483647.
+    if (CounterValue > 2147483647) {
+      Diag(Tok.getLocation(), diag::err_counter_overflow);
+      // Retain the maximal value so we don't issue conversion-related
+      // diagnostics by overflowing into a long long. While this does produce
+      // a duplicate value, there's no way to ignore this error so there's no
+      // translation anyway.
+      CounterValue = 2147483647;
+    }
     OS << CounterValue++;
     Tok.setKind(tok::numeric_constant);
   } else if (II == Ident__has_feature) {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 6acf79acea111..32d0f36d17459 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -225,7 +225,7 @@ bool ChainedASTReaderListener::ReadPreprocessorOptions(
 }
 
 void ChainedASTReaderListener::ReadCounter(const serialization::ModuleFile &M,
-                                           unsigned Value) {
+                                           unsigned long Value) {
   First->ReadCounter(M, Value);
   Second->ReadCounter(M, Value);
 }
@@ -973,7 +973,7 @@ bool PCHValidator::ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
       PP.getPreprocessorOpts());
 }
 
-void PCHValidator::ReadCounter(const ModuleFile &M, unsigned Value) {
+void PCHValidator::ReadCounter(const ModuleFile &M, unsigned long Value) {
   PP.setCounterValue(Value);
 }
 
diff --git a/clang/test/C/C2y/n3457.c b/clang/test/C/C2y/n3457.c
new file mode 100644
index 0000000000000..77e7b7ed5ecfe
--- /dev/null
+++ b/clang/test/C/C2y/n3457.c
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -verify=ext -std=c23 -pedantic %s
+// RUN: %clang_cc1 -verify=pre -std=c2y -pedantic -Wpre-c2y-compat %s
+
+/* WG14 N3457: Clang 22
+ * The __COUNTER__ predefined macro
+ *
+ * This predefined macro was supported as an extension in earlier versions of
+ * Clang, but the required diagnostics for the limits were not added until 22.
+ */
+
+// Ensure that __COUNTER__ starts from 0.
+static_assert(__COUNTER__ == 0); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                    pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                  */
+
+// Ensure that the produced value can be used with token concatenation.
+#define CAT_IMPL(a, b) a ## b
+#define CAT(a, b) CAT_IMPL(a, b)
+#define NAME_WITH_COUNTER(a) CAT(a, __COUNTER__)
+void test() {
+  // Because this is the 2nd expansion, this defines test1.
+  int NAME_WITH_COUNTER(test); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                  pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                */
+  int other_test = test1;      // Ok
+}
+
+// Ensure that __COUNTER__ increments each time you mention it.
+static_assert(__COUNTER__ == 2); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                    pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                 */
+static_assert(__COUNTER__ == 3); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                    pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                 */
+static_assert(__COUNTER__ == 4); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                    pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                 */
diff --git a/clang/test/C/C2y/n3457_1.c b/clang/test/C/C2y/n3457_1.c
new file mode 100644
index 0000000000000..2612d096e23fc
--- /dev/null
+++ b/clang/test/C/C2y/n3457_1.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -verify -std=c2y -finitial-counter-value=2147483646 %s
+
+// The value produced needs to be a type that's representable with a signed
+// long. However, the actual type it expands to does *not* need to be forced to
+// be signed long because that would generally mean suffixing the value with L,
+// which would be very surprising for folks using this to generate unique ids.
+// We'll test this by ensuring the largest value can be expanded properly and
+// an assertion that signed long is always at least four bytes wide (which is
+// what's required to represent that maximal value).
+//
+// So we set the initial counter value to 2147483646, we'll validate that,
+// increment it once to get to the maximal value and ensure there's no
+// diagnostic, then increment again to ensure we get the constraint violation.
+
+static_assert(__COUNTER__ == 2147483646); // Test and increment
+static_assert(__COUNTER__ == 2147483647); // Test and increment
+
+// This one should fail.
+signed long i = __COUNTER__; // expected-error {{'__COUNTER__' value cannot exceed 2147483647}}
+
diff --git a/clang/test/C/C2y/n3457_2.c b/clang/test/C/C2y/n3457_2.c
new file mode 100644
index 0000000000000..cf268283e11c6
--- /dev/null
+++ b/clang/test/C/C2y/n3457_2.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -verify=good -std=c2y -finitial-counter-value=2147483648 %s
+// RUN: %clang_cc1 -verify -std=c2y -finitial-counter-value=2147483648 -DEXPAND_IT %s
+// good-no-diagnostics
+
+// This sets the intial __COUNTER__ value to something that's too big. Setting
+// the value too large is fine. Expanding to a too-large value is not.
+#ifdef EXPAND_IT
+  // This one should fail.
+  signed long i = __COUNTER__; // expected-error {{'__COUNTER__' value cannot exceed 2147483647}}
+#endif
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 380f66495a367..96dcaf14aae02 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -329,7 +329,7 @@ <h2 id="c2y">C2y implementation status</h2>
     <tr>
       <td>The __COUNTER__ predefined macro</td>
       <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm">N3457</a></td>
-      <td class="unknown" align="center">Unknown</td>
+      <td class="full" align="center">Clang 22</td>
 	</tr>
     <tr>
       <td>Chasing Ghosts I: constant expressions v2</td>

>From 1d0a459b44489e6a1471464571bdfffe97158d1b Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Thu, 9 Oct 2025 12:11:52 -0400
Subject: [PATCH 2/5] Clang 22 isn't released yet

---
 clang/www/c_status.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 96dcaf14aae02..eeff464313245 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -329,7 +329,7 @@ <h2 id="c2y">C2y implementation status</h2>
     <tr>
       <td>The __COUNTER__ predefined macro</td>
       <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm">N3457</a></td>
-      <td class="full" align="center">Clang 22</td>
+      <td class="unreleased" align="center">Clang 22</td>
 	</tr>
     <tr>
       <td>Chasing Ghosts I: constant expressions v2</td>

>From 7129a6ad493ca1a33b388eb4e6b6bde406cf9dd0 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Thu, 9 Oct 2025 12:13:15 -0400
Subject: [PATCH 3/5] Use a named constant; NFC

---
 clang/lib/Lex/PPMacroExpansion.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index 37eef9060d4b5..8213e2bc2d769 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1741,13 +1741,14 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
          getLangOpts().C2y ? diag::warn_counter : diag::ext_counter);
     // __COUNTER__ expands to a simple numeric value that must be less than
     // 2147483647.
-    if (CounterValue > 2147483647) {
+    constexpr unsigned long MaxPosValue = std::numeric_limits<int32_t>::max();
+    if (CounterValue > MaxPosValue) {
       Diag(Tok.getLocation(), diag::err_counter_overflow);
       // Retain the maximal value so we don't issue conversion-related
       // diagnostics by overflowing into a long long. While this does produce
       // a duplicate value, there's no way to ignore this error so there's no
       // translation anyway.
-      CounterValue = 2147483647;
+      CounterValue = MaxPosValue;
     }
     OS << CounterValue++;
     Tok.setKind(tok::numeric_constant);

>From 1c123deb64d25e7e7f0966f37b73165a92916ed2 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Thu, 9 Oct 2025 12:14:28 -0400
Subject: [PATCH 4/5] Add C++ pedantic test

---
 clang/test/C/C2y/n3457.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/test/C/C2y/n3457.c b/clang/test/C/C2y/n3457.c
index 77e7b7ed5ecfe..d71a3f37e1343 100644
--- a/clang/test/C/C2y/n3457.c
+++ b/clang/test/C/C2y/n3457.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -verify=ext -std=c23 -pedantic %s
+// RUN: %clang_cc1 -verify=ext -pedantic -x c++ %s
 // RUN: %clang_cc1 -verify=pre -std=c2y -pedantic -Wpre-c2y-compat %s
 
 /* WG14 N3457: Clang 22

>From 07207716fe0409e45d3383afef71867973974997 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Thu, 9 Oct 2025 12:15:37 -0400
Subject: [PATCH 5/5] Update Language Extensions docs for backporting

---
 clang/docs/LanguageExtensions.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 54c215e9ccfaa..7fdc991af3b60 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1823,6 +1823,7 @@ Octal literals prefixed with ``0o`` or ``0O``                                  C
 ``_Countof`` (N3369, N3469)                                                    C2y           C89
 ``_Generic`` with a type operand (N3260)                                       C2y           C89, C++
 ``++``/``--`` on ``_Complex`` value (N3259)                                    C2y           C89, C++
+``__COUNTER__`` (N3457)                                                        C2y           C89, C++
 ============================================= ================================ ============= =============
 
 Builtin type aliases



More information about the cfe-commits mailing list