diff mbox series

cpp: new built-in __EXP_COUNTER__

Message ID a790cf091d7fa1c57d4a823651888cda@kylheku.com
State New
Headers show
Series cpp: new built-in __EXP_COUNTER__ | expand

Commit Message

Kaz Kylheku April 21, 2022, 11:31 a.m. UTC
libcpp/ChangeLog
2022-04-21  Kaz Kylheku  <kaz@kylheku.com>

	This change introduces a pair of related macros
	__EXP_COUNTER__ and __UEXP_COUNTER__.  These macros access
	integer values which enumerate macro expansions.
	They can be used for the purposes of obtaining, unique
	identifiers (within the scope of a translation unit), as a
	replacement for unreliable hacks based on __LINE__.

	Outside of macro expansions, these macros expand to 1,
	so they are easy to test for in portable code that needs
	to fall back on something, like __LINE__.

	* gcc/doc/cpp.texi (__EXP_COUNTER__, __UEXP_COUNTER__):
	Special built-in macros documented.

	* libcpp/include/cpplib.h (struct cpp_macro): New members of
	type long long: exp_number and uexp_number.  These members are
	used to attach the current exp_counter value, and the parent
	context's copy of it to a new macro expansion.  The special
	macros then just access these.
	(enum cpp_builtin_type): New enumeration members,
	BT_EXP_COUNTER and BT_UEXP_COUNTER.

	* libcpp/macro.cc (exp_counter): New static variable for
	counting expansions.  There is an existing one,
	num_expanded_macros_counter, but that has its own purpose and
	is incremented in a specific place.  Plus it uses a narrower
	integer type.
	(_cpp_builtin_number_text): Change the local variable "number"
	from linenum_type to unsigned long long, so it holds at least
	64 bit values.  Handle the BT_EXP_COUNTER and BT_UEXP_COUNTER
	cases.  These just have to see if there is a current macro, and
	retrieve the values from it, otherwise do nothing so that the
	default 1 is produced.  In the case of BT_UEXP_COUNTER, if the
	value is zero, we don't use it, so 1 emerges.  The sprintf of
	the number is adjusted to use the right conversion specifier
	for the wider type.  Space is already being reserved for
	a 64 bit decimal.
	(enter_macro_context): After processing the macro arguments,
	if any, we increment exp_counter and attach its new value to
	the macro's context structure in the exp_number member.  We
	also calculate the macro's uexp_number: the parent context's
	exp_number.  This is tricky: we have to chase the previous
	macro context.  This works if the macro is object-like, or has
	no parameters.  If it has parameters, there is a parameter
	context, and so we have to climb one more flight of stairs to
	get to the real context.

gcc/testsuite/ChangeLog
2022-04-21  Kaz Kylheku  <kaz@kylheku.com>

	* gcc.dg/cpp/expcounter1.c: New test.
	* gcc.dg/cpp/expcounter2.c: New test.
	* gcc.dg/cpp/expcounter3.c: New test.
	* gcc.dg/cpp/expcounter4.c: New test.
	* gcc.dg/cpp/expcounter5.c: New test.

Signed-off-by: Kaz Kylheku <kaz@kylheku.com>
---
  gcc/doc/cpp.texi                       | 81 ++++++++++++++++++++++++++
  gcc/testsuite/ChangeLog                |  8 +++
  gcc/testsuite/gcc.dg/cpp/expcounter1.c | 16 +++++
  gcc/testsuite/gcc.dg/cpp/expcounter2.c | 21 +++++++
  gcc/testsuite/gcc.dg/cpp/expcounter3.c | 22 +++++++
  gcc/testsuite/gcc.dg/cpp/expcounter4.c | 22 +++++++
  gcc/testsuite/gcc.dg/cpp/expcounter5.c | 28 +++++++++
  libcpp/ChangeLog                       | 49 ++++++++++++++++
  libcpp/include/cpplib.h                |  8 +++
  libcpp/init.cc                         |  2 +
  libcpp/macro.cc                        | 44 +++++++++++++-
  11 files changed, 299 insertions(+), 2 deletions(-)
  create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter1.c
  create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter2.c
  create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter3.c
  create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter4.c
  create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter5.c

  unsigned num_expanded_macros_counter = 0;
@@ -490,7 +494,7 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode 
*node,
  			 location_t loc)
  {
    const uchar *result = NULL;
-  linenum_type number = 1;
+  unsigned long long number = 1;

    switch (node->value.builtin)
      {
@@ -660,6 +664,26 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode 
*node,
        number = pfile->counter++;
        break;

+    case BT_EXP_COUNTER:
+      {
+	cpp_hashnode *macro = macro_of_context (pfile->context);
+	if (macro != NULL)
+	  number = macro->value.macro->exp_number;
+      }
+      break;
+
+    case BT_UEXP_COUNTER:
+      {
+	cpp_hashnode *macro = macro_of_context (pfile->context);
+	if (macro != NULL)
+	  {
+	    unsigned long long ue = macro->value.macro->uexp_number;
+	    if (ue > 0)
+	      number = ue;
+	  }
+      }
+      break;
+
      case BT_HAS_ATTRIBUTE:
        number = pfile->cb.has_attribute (pfile, false);
        break;
@@ -683,7 +707,7 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode 
*node,
      {
        /* 21 bytes holds all NUL-terminated unsigned 64-bit numbers.  */
        result = _cpp_unaligned_alloc (pfile, 21);
-      sprintf ((char *) result, "%u", number);
+      sprintf ((char *) result, "%llu", number);
      }

    return result;
@@ -1492,6 +1516,22 @@ enter_macro_context (cpp_reader *pfile, cpp_hashnode 
*node,
        /* Disable the macro within its expansion.  */
        node->flags |= NODE_DISABLED;

+      /* Increment and capture __EXP_COUNTER__ counter.  */
+      macro->exp_number = ++exp_counter;
+
+      /* If we are in the middle of an existing macro, get *its*
+	 exp_number into uexp_number, for __UEXP_COUNTER__. */
+      {
+	cpp_hashnode *existing_macro = macro_of_context (pfile->context);
+	cpp_macro *pmac = existing_macro != NULL
+	                  ? existing_macro->value.macro
+			  : NULL;
+	if (pmac != NULL && pmac->paramc > 0)
+	  existing_macro = macro_of_context (pfile->context->prev);
+	if (existing_macro != NULL)
+	  macro->uexp_number = existing_macro->value.macro->exp_number;
+      }
+
        /* Laziness can only affect the expansion tokens of the macro,
  	 not its fun-likeness or parameters.  */
        _cpp_maybe_notify_macro_use (pfile, node, location);

Comments

Jonathan Wakely March 18, 2024, 7:30 a.m. UTC | #1
On 21/04/22 04:31 -0700, Kaz Kylheku wrote:
>libcpp/ChangeLog
>2022-04-21  Kaz Kylheku  <kaz@kylheku.com>
>
>	This change introduces a pair of related macros
>	__EXP_COUNTER__ and __UEXP_COUNTER__.  These macros access
>	integer values which enumerate macro expansions.
>	They can be used for the purposes of obtaining, unique
>	identifiers (within the scope of a translation unit), as a
>	replacement for unreliable hacks based on __LINE__.
>
>	Outside of macro expansions, these macros expand to 1,
>	so they are easy to test for in portable code that needs
>	to fall back on something, like __LINE__.
>
>	* gcc/doc/cpp.texi (__EXP_COUNTER__, __UEXP_COUNTER__):
>	Special built-in macros documented.
>
>	* libcpp/include/cpplib.h (struct cpp_macro): New members of
>	type long long: exp_number and uexp_number.  These members are
>	used to attach the current exp_counter value, and the parent
>	context's copy of it to a new macro expansion.  The special
>	macros then just access these.
>	(enum cpp_builtin_type): New enumeration members,
>	BT_EXP_COUNTER and BT_UEXP_COUNTER.
>
>	* libcpp/macro.cc (exp_counter): New static variable for
>	counting expansions.  There is an existing one,
>	num_expanded_macros_counter, but that has its own purpose and
>	is incremented in a specific place.  Plus it uses a narrower
>	integer type.
>	(_cpp_builtin_number_text): Change the local variable "number"
>	from linenum_type to unsigned long long, so it holds at least
>	64 bit values.  Handle the BT_EXP_COUNTER and BT_UEXP_COUNTER
>	cases.  These just have to see if there is a current macro, and
>	retrieve the values from it, otherwise do nothing so that the
>	default 1 is produced.  In the case of BT_UEXP_COUNTER, if the
>	value is zero, we don't use it, so 1 emerges.  The sprintf of
>	the number is adjusted to use the right conversion specifier
>	for the wider type.  Space is already being reserved for
>	a 64 bit decimal.
>	(enter_macro_context): After processing the macro arguments,
>	if any, we increment exp_counter and attach its new value to
>	the macro's context structure in the exp_number member.  We
>	also calculate the macro's uexp_number: the parent context's
>	exp_number.  This is tricky: we have to chase the previous
>	macro context.  This works if the macro is object-like, or has
>	no parameters.  If it has parameters, there is a parameter
>	context, and so we have to climb one more flight of stairs to
>	get to the real context.
>
>gcc/testsuite/ChangeLog
>2022-04-21  Kaz Kylheku  <kaz@kylheku.com>
>
>	* gcc.dg/cpp/expcounter1.c: New test.
>	* gcc.dg/cpp/expcounter2.c: New test.
>	* gcc.dg/cpp/expcounter3.c: New test.
>	* gcc.dg/cpp/expcounter4.c: New test.
>	* gcc.dg/cpp/expcounter5.c: New test.
>
>Signed-off-by: Kaz Kylheku <kaz@kylheku.com>
>---
> gcc/doc/cpp.texi                       | 81 ++++++++++++++++++++++++++
> gcc/testsuite/ChangeLog                |  8 +++
> gcc/testsuite/gcc.dg/cpp/expcounter1.c | 16 +++++
> gcc/testsuite/gcc.dg/cpp/expcounter2.c | 21 +++++++
> gcc/testsuite/gcc.dg/cpp/expcounter3.c | 22 +++++++
> gcc/testsuite/gcc.dg/cpp/expcounter4.c | 22 +++++++
> gcc/testsuite/gcc.dg/cpp/expcounter5.c | 28 +++++++++
> libcpp/ChangeLog                       | 49 ++++++++++++++++
> libcpp/include/cpplib.h                |  8 +++
> libcpp/init.cc                         |  2 +
> libcpp/macro.cc                        | 44 +++++++++++++-
> 11 files changed, 299 insertions(+), 2 deletions(-)
> create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter1.c
> create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter2.c
> create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter3.c
> create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter4.c
> create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter5.c
>
>diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
>index 90b2767e39a..d52450958d7 100644
>--- a/gcc/doc/cpp.texi
>+++ b/gcc/doc/cpp.texi
>@@ -1941,6 +1941,87 @@ generate unique identifiers.  Care must be 
>taken to ensure that
> @code{__COUNTER__} is not expanded prior to inclusion of precompiled headers
> which use it.  Otherwise, the precompiled headers will not be used.
>
>+@item __EXP_COUNTER__
>+This macro's name means "(macro) expansion counter".
>+Outside of macro replacement sequences, it expands to the integer
>+token @code{1}.  This make it possible to easily test for the presence
>+of this feature using conditional directives such as
>+@code{#if __EXP_COUNTER__}.

It's a macro, so you can just use '#ifdef __EXP_COUNTER__' to test if
it's supported. Is this additional behaviour necessary?

>+When @code{__EXP_COUNTER__} occurs in the replacement token sequence
>+of a macro, it expands to positive decimal integer token which

expands to _a_ positive decimal integer token?

>+uniquely identifies the expansion, within a translation unit.
>+Unlike @code{__COUNTER__}, @code{__EXP_COUNTER__} does not increment
>+on each access: all references to it which occur in the same token
>+replacement sequence of the same instance of a macro being expanded
>+produce the same integer token.
>+
>+The implementation of this feature works as follows: a counter is 
>initialized
>+to zero prior to the processing of any tokens of the translation 
>unit.  Whenever
>+a macro is about to be expanded, the counter is incremented, and the 
>counter's
>+value is associated with that macro expansion context.  Subsequent 
>increments of
>+the counter, such as during rescanning of a token sequence for more 
>macros, do
>+not affect the captured value.  Nested macro expansions are associated with
>+their own @code{__EXP_COUNTER__} values.  The counter uses the
>+@code{unsigned long long} type of the host implementation for which the
>+preprocessor is compiled.  If this counter overflows and 
>@code{__EXP_COUNTER__}
>+is subsequently accessed, the behavior is unspecified.
>+
>+The main use for @code{__EXP_COUNTER__} is the generation of unique symbols,
>+as in the following example:
>+
>+@smallexample
>+#define cat(a, b) a ## b
>+#define xcat(a, b) cat (a, b)
>+#define uni(pfx, count) xcat (pfx, xcat (_uniq_, count))
>+
>+#define repeat(count)                                       \
>+  for (int uni (i, __EXP_COUNTER__) = 0,                    \
>+       uni (c, __EXP_COUNTER__) = (count);                  \
>+       uni (i, __EXP_COUNTER__) < uni (c, __EXP_COUNTER__); \
>+       uni (i, __EXP_COUNTER__)++)
>+
>+repeat (get_repeat_count ())
>+  puts ("Hello, world!")
>+@end smallexample
>+
>+@item __UEXP_COUNTER__
>+This macro is closely related to @code{__EXP_COUNTER__}.  In the
>+expansion of a macro which is being expanded in the middle of another
>+macro expansion, @code{__UEXP_COUNTER__} ("upper context expansion counter")
>+produces the @code{__EXP_COUNTER__} value of the parent expansion.
>+In any other situation, this macro expands to @code{1}.
>+
>+Consider the following example:
>+
>+@smallexample
>+#define child __UEXP_COUNTER__ __EXP_COUNTER__
>+#define parent @{ __EXP_COUNTER__ child @}
>+parent
>+@end smallexample
>+
>+Here, @code{parent} will expand to a sequence similar to @code{@{ 42 
>42 43 @}}.
>+The @code{__UEXP_COUNTER__} value from @code{child} matches the
>+@code{__EXP_COUNTER__} in @code{parent}, but the @code{child}'s own
>+@code{__EXP_COUNTER__} is, of course, different.
>+
>+The main use for @code{__UEXP_COUNTER__} is the simplification of macros
>+that require @code{__EXP_COUNTER__}.  The example given for
>+@code{__EXP_COUNTER__} can be simplified like this:
>+
>+@smallexample
>+#define cat(a, b) a ## b
>+#define xcat(a, b) cat (a, b)
>+#define uni(pfx) xcat (pfx, xcat (_uniq_, __UEXP_COUNTER__))
>+
>+#define repeat(count)                       \
>+  for (int uni (i) = 0, uni (c) = (count);  \
>+       uni (i) < uni (c);                   \
>+       uni (i)++)
>+
>+repeat (get_repeat_count ())
>+  puts ("Hello, world!")
>+@end smallexample
>+
> @item __GFORTRAN__
> The GNU Fortran compiler defines this.
>
>diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
>index e147f69c8eb..fee0ae0ad4b 100644
>--- a/gcc/testsuite/ChangeLog
>+++ b/gcc/testsuite/ChangeLog
>@@ -1,3 +1,11 @@
>+2022-04-21  Kaz Kylheku  <kaz@kylheku.com>
>+
>+	* gcc.dg/cpp/expcounter1.c: New test.
>+	* gcc.dg/cpp/expcounter2.c: New test.
>+	* gcc.dg/cpp/expcounter3.c: New test.
>+	* gcc.dg/cpp/expcounter4.c: New test.
>+	* gcc.dg/cpp/expcounter5.c: New test.
>+
> 2022-04-15  Paul A. Clarke  <pc@us.ibm.com>
>
> 	* g++.dg/debug/dwarf2/const2.C: Move to g++.target/powerpc.

ChangeLog files are autogenerated every night, please don't include
modifications to them in patch submissions (it's not necessary, and it
means the patch won't apply cleanly because the ChangeLog will have
moved on since you created the patch).

I don't have an opinion on the implementation, or the proposal itself,
except that the implementation seems susprisingly simple, which is
nice.
Jonathan Wakely March 18, 2024, 7:32 a.m. UTC | #2
On 18/03/24 07:30 +0000, Jonathan Wakely wrote:
>On 21/04/22 04:31 -0700, Kaz Kylheku wrote:
>>libcpp/ChangeLog
>>2022-04-21  Kaz Kylheku  <kaz@kylheku.com>
>>
>>	This change introduces a pair of related macros
>>	__EXP_COUNTER__ and __UEXP_COUNTER__.  These macros access
>>	integer values which enumerate macro expansions.
>>	They can be used for the purposes of obtaining, unique
>>	identifiers (within the scope of a translation unit), as a
>>	replacement for unreliable hacks based on __LINE__.
>>
>>	Outside of macro expansions, these macros expand to 1,
>>	so they are easy to test for in portable code that needs
>>	to fall back on something, like __LINE__.
>>
>>	* gcc/doc/cpp.texi (__EXP_COUNTER__, __UEXP_COUNTER__):
>>	Special built-in macros documented.
>>
>>	* libcpp/include/cpplib.h (struct cpp_macro): New members of
>>	type long long: exp_number and uexp_number.  These members are
>>	used to attach the current exp_counter value, and the parent
>>	context's copy of it to a new macro expansion.  The special
>>	macros then just access these.
>>	(enum cpp_builtin_type): New enumeration members,
>>	BT_EXP_COUNTER and BT_UEXP_COUNTER.
>>
>>	* libcpp/macro.cc (exp_counter): New static variable for
>>	counting expansions.  There is an existing one,
>>	num_expanded_macros_counter, but that has its own purpose and
>>	is incremented in a specific place.  Plus it uses a narrower
>>	integer type.
>>	(_cpp_builtin_number_text): Change the local variable "number"
>>	from linenum_type to unsigned long long, so it holds at least
>>	64 bit values.  Handle the BT_EXP_COUNTER and BT_UEXP_COUNTER
>>	cases.  These just have to see if there is a current macro, and
>>	retrieve the values from it, otherwise do nothing so that the
>>	default 1 is produced.  In the case of BT_UEXP_COUNTER, if the
>>	value is zero, we don't use it, so 1 emerges.  The sprintf of
>>	the number is adjusted to use the right conversion specifier
>>	for the wider type.  Space is already being reserved for
>>	a 64 bit decimal.
>>	(enter_macro_context): After processing the macro arguments,
>>	if any, we increment exp_counter and attach its new value to
>>	the macro's context structure in the exp_number member.  We
>>	also calculate the macro's uexp_number: the parent context's
>>	exp_number.  This is tricky: we have to chase the previous
>>	macro context.  This works if the macro is object-like, or has
>>	no parameters.  If it has parameters, there is a parameter
>>	context, and so we have to climb one more flight of stairs to
>>	get to the real context.
>>
>>gcc/testsuite/ChangeLog
>>2022-04-21  Kaz Kylheku  <kaz@kylheku.com>
>>
>>	* gcc.dg/cpp/expcounter1.c: New test.
>>	* gcc.dg/cpp/expcounter2.c: New test.
>>	* gcc.dg/cpp/expcounter3.c: New test.
>>	* gcc.dg/cpp/expcounter4.c: New test.
>>	* gcc.dg/cpp/expcounter5.c: New test.
>>
>>Signed-off-by: Kaz Kylheku <kaz@kylheku.com>
>>---
>>gcc/doc/cpp.texi                       | 81 ++++++++++++++++++++++++++
>>gcc/testsuite/ChangeLog                |  8 +++
>>gcc/testsuite/gcc.dg/cpp/expcounter1.c | 16 +++++
>>gcc/testsuite/gcc.dg/cpp/expcounter2.c | 21 +++++++
>>gcc/testsuite/gcc.dg/cpp/expcounter3.c | 22 +++++++
>>gcc/testsuite/gcc.dg/cpp/expcounter4.c | 22 +++++++
>>gcc/testsuite/gcc.dg/cpp/expcounter5.c | 28 +++++++++
>>libcpp/ChangeLog                       | 49 ++++++++++++++++
>>libcpp/include/cpplib.h                |  8 +++
>>libcpp/init.cc                         |  2 +
>>libcpp/macro.cc                        | 44 +++++++++++++-
>>11 files changed, 299 insertions(+), 2 deletions(-)
>>create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter1.c
>>create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter2.c
>>create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter3.c
>>create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter4.c
>>create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter5.c
>>
>>diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
>>index 90b2767e39a..d52450958d7 100644
>>--- a/gcc/doc/cpp.texi
>>+++ b/gcc/doc/cpp.texi
>>@@ -1941,6 +1941,87 @@ generate unique identifiers.  Care must be 
>>taken to ensure that
>>@code{__COUNTER__} is not expanded prior to inclusion of precompiled headers
>>which use it.  Otherwise, the precompiled headers will not be used.
>>
>>+@item __EXP_COUNTER__
>>+This macro's name means "(macro) expansion counter".
>>+Outside of macro replacement sequences, it expands to the integer
>>+token @code{1}.  This make it possible to easily test for the presence
>>+of this feature using conditional directives such as
>>+@code{#if __EXP_COUNTER__}.
>
>It's a macro, so you can just use '#ifdef __EXP_COUNTER__' to test if
>it's supported. Is this additional behaviour necessary?
>
>>+When @code{__EXP_COUNTER__} occurs in the replacement token sequence
>>+of a macro, it expands to positive decimal integer token which
>
>expands to _a_ positive decimal integer token?
>
>>+uniquely identifies the expansion, within a translation unit.
>>+Unlike @code{__COUNTER__}, @code{__EXP_COUNTER__} does not increment
>>+on each access: all references to it which occur in the same token
>>+replacement sequence of the same instance of a macro being expanded
>>+produce the same integer token.
>>+
>>+The implementation of this feature works as follows: a counter is 
>>initialized
>>+to zero prior to the processing of any tokens of the translation 
>>unit.  Whenever
>>+a macro is about to be expanded, the counter is incremented, and 
>>the counter's
>>+value is associated with that macro expansion context.  Subsequent 
>>increments of
>>+the counter, such as during rescanning of a token sequence for more 
>>macros, do
>>+not affect the captured value.  Nested macro expansions are associated with
>>+their own @code{__EXP_COUNTER__} values.  The counter uses the
>>+@code{unsigned long long} type of the host implementation for which the
>>+preprocessor is compiled.  If this counter overflows and 
>>@code{__EXP_COUNTER__}
>>+is subsequently accessed, the behavior is unspecified.
>>+
>>+The main use for @code{__EXP_COUNTER__} is the generation of unique symbols,
>>+as in the following example:
>>+
>>+@smallexample
>>+#define cat(a, b) a ## b
>>+#define xcat(a, b) cat (a, b)
>>+#define uni(pfx, count) xcat (pfx, xcat (_uniq_, count))
>>+
>>+#define repeat(count)                                       \
>>+  for (int uni (i, __EXP_COUNTER__) = 0,                    \
>>+       uni (c, __EXP_COUNTER__) = (count);                  \
>>+       uni (i, __EXP_COUNTER__) < uni (c, __EXP_COUNTER__); \
>>+       uni (i, __EXP_COUNTER__)++)
>>+
>>+repeat (get_repeat_count ())
>>+  puts ("Hello, world!")
>>+@end smallexample
>>+
>>+@item __UEXP_COUNTER__
>>+This macro is closely related to @code{__EXP_COUNTER__}.  In the
>>+expansion of a macro which is being expanded in the middle of another
>>+macro expansion, @code{__UEXP_COUNTER__} ("upper context expansion counter")
>>+produces the @code{__EXP_COUNTER__} value of the parent expansion.
>>+In any other situation, this macro expands to @code{1}.
>>+
>>+Consider the following example:
>>+
>>+@smallexample
>>+#define child __UEXP_COUNTER__ __EXP_COUNTER__
>>+#define parent @{ __EXP_COUNTER__ child @}
>>+parent
>>+@end smallexample
>>+
>>+Here, @code{parent} will expand to a sequence similar to @code{@{ 
>>42 42 43 @}}.
>>+The @code{__UEXP_COUNTER__} value from @code{child} matches the
>>+@code{__EXP_COUNTER__} in @code{parent}, but the @code{child}'s own
>>+@code{__EXP_COUNTER__} is, of course, different.
>>+
>>+The main use for @code{__UEXP_COUNTER__} is the simplification of macros
>>+that require @code{__EXP_COUNTER__}.  The example given for
>>+@code{__EXP_COUNTER__} can be simplified like this:
>>+
>>+@smallexample
>>+#define cat(a, b) a ## b
>>+#define xcat(a, b) cat (a, b)
>>+#define uni(pfx) xcat (pfx, xcat (_uniq_, __UEXP_COUNTER__))
>>+
>>+#define repeat(count)                       \
>>+  for (int uni (i) = 0, uni (c) = (count);  \
>>+       uni (i) < uni (c);                   \
>>+       uni (i)++)
>>+
>>+repeat (get_repeat_count ())
>>+  puts ("Hello, world!")
>>+@end smallexample
>>+
>>@item __GFORTRAN__
>>The GNU Fortran compiler defines this.
>>
>>diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
>>index e147f69c8eb..fee0ae0ad4b 100644
>>--- a/gcc/testsuite/ChangeLog
>>+++ b/gcc/testsuite/ChangeLog
>>@@ -1,3 +1,11 @@
>>+2022-04-21  Kaz Kylheku  <kaz@kylheku.com>
>>+
>>+	* gcc.dg/cpp/expcounter1.c: New test.
>>+	* gcc.dg/cpp/expcounter2.c: New test.
>>+	* gcc.dg/cpp/expcounter3.c: New test.
>>+	* gcc.dg/cpp/expcounter4.c: New test.
>>+	* gcc.dg/cpp/expcounter5.c: New test.
>>+
>>2022-04-15  Paul A. Clarke  <pc@us.ibm.com>
>>
>>	* g++.dg/debug/dwarf2/const2.C: Move to g++.target/powerpc.
>
>ChangeLog files are autogenerated every night, please don't include
>modifications to them in patch submissions (it's not necessary, and it
>means the patch won't apply cleanly because the ChangeLog will have
>moved on since you created the patch).
>
>I don't have an opinion on the implementation, or the proposal itself,
>except that the implementation seems susprisingly simple, which is
>nice.

Oh I see there's a [PATCH v2] at
https://gcc.gnu.org/pipermail/gcc-patches/2022-April/593473.html
although I think my brief comments apply to that one too.
Kaz Kylheku March 18, 2024, 4:39 p.m. UTC | #3
On 2024-03-18 00:30, Jonathan Wakely wrote:
>>+@item __EXP_COUNTER__
>>+This macro's name means "(macro) expansion counter".
>>+Outside of macro replacement sequences, it expands to the integer
>>+token @code{1}.  This make it possible to easily test for the presence
>>+of this feature using conditional directives such as
>>+@code{#if __EXP_COUNTER__}.
> 
> It's a macro, so you can just use '#ifdef __EXP_COUNTER__' to test if
> it's supported. Is this additional behaviour necessary?

Thanks for looking at the patch.

The macro has to be defined, but it doesn't have to be 1.

Outside a macro body, it could just appear defined
with an empty value, as if by #define __EXP_COUNTER__.

When I dump the predefined macros of a nearby GCC installation, I
see very few empty ones:

   $ echo | gcc -dM -E - | awk 'NF == 2'
   #define __USER_LABEL_PREFIX__
   #define __REGISTER_PREFIX__

The __EXP_COUNTER__ and __UEXP_COUNTER__ symbols are
intended for suffix and infix use, so they are roughly in
a kind of category with those those two.

I will make that change, and also fix the grammar error
and remove the conflict-promoting ChangeLog changes.
Jonathan Wakely March 18, 2024, 7:28 p.m. UTC | #4
On Mon, 18 Mar 2024 at 16:46, Kaz Kylheku <kaz@kylheku.com> wrote:
>
> On 2024-03-18 00:30, Jonathan Wakely wrote:
> >>+@item __EXP_COUNTER__
> >>+This macro's name means "(macro) expansion counter".
> >>+Outside of macro replacement sequences, it expands to the integer
> >>+token @code{1}.  This make it possible to easily test for the presence
> >>+of this feature using conditional directives such as
> >>+@code{#if __EXP_COUNTER__}.
> >
> > It's a macro, so you can just use '#ifdef __EXP_COUNTER__' to test if
> > it's supported. Is this additional behaviour necessary?
>
> Thanks for looking at the patch.
>
> The macro has to be defined, but it doesn't have to be 1.
>
> Outside a macro body, it could just appear defined
> with an empty value, as if by #define __EXP_COUNTER__.
>
> When I dump the predefined macros of a nearby GCC installation, I
> see very few empty ones:
>
>    $ echo | gcc -dM -E - | awk 'NF == 2'
>    #define __USER_LABEL_PREFIX__
>    #define __REGISTER_PREFIX__
>
> The __EXP_COUNTER__ and __UEXP_COUNTER__ symbols are
> intended for suffix and infix use, so they are roughly in
> a kind of category with those those two.

I don't feel strongly about the value, it seems fine to define it as 1
too. I just thought it was a bit strange to explicitly mention testing
its value to detect support, when #ifdef seems simpler and more
"correct" (as in, it just checks if it's defined, and thevalue is
irrelevant).

>
> I will make that change, and also fix the grammar error
> and remove the conflict-promoting ChangeLog changes.
>
Kaz Kylheku March 19, 2024, 5:27 p.m. UTC | #5
On 2024-03-18 00:30, Jonathan Wakely wrote:
> I don't have an opinion on the implementation, or the proposal itself,
> except that the implementation seems susprisingly simple, which is
> nice.

Hi Jonathan,

Here is an updated patch.

It rebased cleanly over more than newer 16000 commits, suggesting
that the area in the cpp code is "still waters", which is good.

I made the documentation change not to recommend using #if, but
#ifdef.

I got rid of the ChangeLog changes, and also tried to pay more
attention to the log message format, where the ChangeLog pieces
are specified.

In the first test case, I had to adjust the expected warning text
for two lines.
Bernhard Reutner-Fischer March 20, 2024, 11:34 p.m. UTC | #6
On 19 March 2024 18:27:13 CET, Kaz Kylheku <kaz@kylheku.com> wrote:
>On 2024-03-18 00:30, Jonathan Wakely wrote:
>> I don't have an opinion on the implementation, or the proposal itself,
>> except that the implementation seems susprisingly simple, which is
>> nice.
>
>Hi Jonathan,
>
>Here is an updated patch.
>
>It rebased cleanly over more than newer 16000 commits, suggesting
>that the area in the cpp code is "still waters", which is good.
>
>I made the documentation change not to recommend using #if, but
>#ifdef.
>
>I got rid of the ChangeLog changes, and also tried to pay more
>attention to the log message format, where the ChangeLog pieces
>are specified.
>
>In the first test case, I had to adjust the expected warning text
>for two lines.
>

Please forgive the bike shedding, but __EXP_COUNTER__ would lead me into thinking about exponents or thereabouts.
__MACRO_EXPANSION_COUNTER__ is more what your patch is about, IMHO? Maybe you could come up with a more descriptive name, please?

And, while I can see what could possibly be done with that, I'm not really convinced that it would be a wise idea to (unilaterally) support that idea. Don't you think that this would encourage producing more spaghetti code?

Just curious about real world motivating examples I guess.
cheers
Kaz Kylheku March 22, 2024, 12:18 a.m. UTC | #7
On 2024-03-20 16:34, rep.dot.nop@gmail.com wrote:
> On 19 March 2024 18:27:13 CET, Kaz Kylheku <kaz@kylheku.com> wrote:
>>On 2024-03-18 00:30, Jonathan Wakely wrote:
>>> I don't have an opinion on the implementation, or the proposal itself,
>>> except that the implementation seems susprisingly simple, which is
>>> nice.
>>
>>Hi Jonathan,
>>
>>Here is an updated patch.
>>
>>It rebased cleanly over more than newer 16000 commits, suggesting
>>that the area in the cpp code is "still waters", which is good.
>>
>>I made the documentation change not to recommend using #if, but
>>#ifdef.
>>
>>I got rid of the ChangeLog changes, and also tried to pay more
>>attention to the log message format, where the ChangeLog pieces
>>are specified.
>>
>>In the first test case, I had to adjust the expected warning text
>>for two lines.
>>
> 
> Please forgive the bike shedding, but __EXP_COUNTER__ would lead me into thinking about exponents or thereabouts.
> __MACRO_EXPANSION_COUNTER__ is more what your patch is about, IMHO? Maybe you could come up with a more descriptive name, please?
> 
> And, while I can see what could possibly be done with that, I'm not really convinced that it would be a wise idea to (unilaterally) support that idea. Don't you think that this would encourage producing more spaghetti code?
> 
> Just curious about real world motivating examples I guess.
> cheers

Hi, (Bernhard?)

Concerns about naming are very important; not bike shedding at all.
I changed the patch to use __EXPANSION_NUMBER__. I didn't include MACRO
because I hope it's clear that in preprocessing, we are expanding
macros. The parent symbol is now called __PARENT_EXPANSION_NUMBER__.

I dropped the COUNTER terminology because the existing __COUNTER__
is a symbol whose value changes each time it is mentioned,
These symbols are not like that; they capture a fixed value in
a scope and behave like ordinary macros.

In doing the renaming, I noticed that from the beginning I've already
been calling the internal value in the macro context macro->exp_number,
because it's not a counter.

The focus of this feature isn't to enable some new "earth-shattering"
techniques, but to improve certain situations in existing macros.

For instance, suppose we have a macro that expands to some block
of code in which there is an internal goto. If we have it

  #define MAC(...) { ... goto _label; ... __label: ; }

then this cannot be used twice in the same function; labels have
function scope. If we make it

  #define MAC(...) { ... goto CAT(__label, __LINE__); ... CAT(__label, __LINE__): ; }

we now can use MAC two or more times in the same function, but not in
the same line of code.

With __EXPANSION_NUMBER__ it is doable. Given this program:

  #define xcat(A, B) A ## B
  #define cat(A, B) xcat(A, B)
  #define lab(PREFIX) cat(PREFIX, __PARENT_EXPANSION_NUMBER__)

  #define MAC { goto lab(foo); /*...*/ lab(foo): ; }

  MAC MAC MAC

We get the preprocessed output (with -E):

  { goto foo3; foo3: ; } { goto foo10; foo10: ; } { goto foo17; foo17: ; }

There are issues with relying on __LINE__ to produce different values
when it is referenced in code generated by a macro.

The following program prints the same value 12 three times; even though
PRINT seems to be referenced on different physical lines in the
PRINT3 macro replacement text. __LINE__ references the line where
the top-level expansion of PRINT3 occurs, not where PRINT occurs.

#include <stdio.h>

#define PRINT (printf("%d\n", __LINE__))

#define PRINT3 do { \
  PRINT;            \
  PRINT;            \
  PRINT;            \
} while (0)

int main()
{
  PRINT3;
  return 0;
}
Andrew Pinski March 22, 2024, 1:40 a.m. UTC | #8
On Thu, Mar 21, 2024, 17:20 Kaz Kylheku <kaz@kylheku.com> wrote:

> On 2024-03-20 16:34, rep.dot.nop@gmail.com wrote:
> > On 19 March 2024 18:27:13 CET, Kaz Kylheku <kaz@kylheku.com> wrote:
> >>On 2024-03-18 00:30, Jonathan Wakely wrote:
> >>> I don't have an opinion on the implementation, or the proposal itself,
> >>> except that the implementation seems susprisingly simple, which is
> >>> nice.
> >>
> >>Hi Jonathan,
> >>
> >>Here is an updated patch.
> >>
> >>It rebased cleanly over more than newer 16000 commits, suggesting
> >>that the area in the cpp code is "still waters", which is good.
> >>
> >>I made the documentation change not to recommend using #if, but
> >>#ifdef.
> >>
> >>I got rid of the ChangeLog changes, and also tried to pay more
> >>attention to the log message format, where the ChangeLog pieces
> >>are specified.
> >>
> >>In the first test case, I had to adjust the expected warning text
> >>for two lines.
> >>
> >
> > Please forgive the bike shedding, but __EXP_COUNTER__ would lead me into
> thinking about exponents or thereabouts.
> > __MACRO_EXPANSION_COUNTER__ is more what your patch is about, IMHO?
> Maybe you could come up with a more descriptive name, please?
> >
> > And, while I can see what could possibly be done with that, I'm not
> really convinced that it would be a wise idea to (unilaterally) support
> that idea. Don't you think that this would encourage producing more
> spaghetti code?
> >
> > Just curious about real world motivating examples I guess.
> > cheers
>
> Hi, (Bernhard?)
>
> Concerns about naming are very important; not bike shedding at all.
> I changed the patch to use __EXPANSION_NUMBER__. I didn't include MACRO
> because I hope it's clear that in preprocessing, we are expanding
> macros. The parent symbol is now called __PARENT_EXPANSION_NUMBER__.
>
> I dropped the COUNTER terminology because the existing __COUNTER__
> is a symbol whose value changes each time it is mentioned,
> These symbols are not like that; they capture a fixed value in
> a scope and behave like ordinary macros.
>
> In doing the renaming, I noticed that from the beginning I've already
> been calling the internal value in the macro context macro->exp_number,
> because it's not a counter.
>
> The focus of this feature isn't to enable some new "earth-shattering"
> techniques, but to improve certain situations in existing macros.
>
> For instance, suppose we have a macro that expands to some block
> of code in which there is an internal goto. If we have it
>
>   #define MAC(...) { ... goto _label; ... __label: ; }
>
> then this cannot be used twice in the same function; labels have
> function scope.



In this case why can't you use gcc's already extension of defining a local
label?
https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Local-Labels.html

This extension has been around for over 20 years specifically for that use
case.

Thanks,
Andrew



If we make it
>
>   #define MAC(...) { ... goto CAT(__label, __LINE__); ... CAT(__label,
> __LINE__): ; }
>
> we now can use MAC two or more times in the same function, but not in
> the same line of code.
>
> With __EXPANSION_NUMBER__ it is doable. Given this program:
>
>   #define xcat(A, B) A ## B
>   #define cat(A, B) xcat(A, B)
>   #define lab(PREFIX) cat(PREFIX, __PARENT_EXPANSION_NUMBER__)
>
>   #define MAC { goto lab(foo); /*...*/ lab(foo): ; }
>
>   MAC MAC MAC
>
> We get the preprocessed output (with -E):
>
>   { goto foo3; foo3: ; } { goto foo10; foo10: ; } { goto foo17; foo17: ; }
>
> There are issues with relying on __LINE__ to produce different values
> when it is referenced in code generated by a macro.
>
> The following program prints the same value 12 three times; even though
> PRINT seems to be referenced on different physical lines in the
> PRINT3 macro replacement text. __LINE__ references the line where
> the top-level expansion of PRINT3 occurs, not where PRINT occurs.
>
> #include <stdio.h>
>
> #define PRINT (printf("%d\n", __LINE__))
>
> #define PRINT3 do { \
>   PRINT;            \
>   PRINT;            \
>   PRINT;            \
> } while (0)
>
> int main()
> {
>   PRINT3;
>   return 0;
> }
>
Kaz Kylheku March 22, 2024, 6:03 a.m. UTC | #9
On 2024-03-21 18:40, Andrew Pinski wrote:

On Thu, Mar 21, 2024, 17:20 Kaz Kylheku <kaz@kylheku.com> wrote: For instance, suppose we have a macro that expands to some block
of code in which there is an internal goto. If we have it

  #define MAC(...) { ... goto _label; ... __label: ; }

then this cannot be used twice in the same function; labels have
function scope. 

In this case why can't you use gcc's already extension of defining a local label? 
https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Local-Labels.html 

This extension has been around for over 20 years specifically for that use case. 

Yes. For that case, local labels are a nice solution. 

It's just an example of the sort of thing for which it may be useful 
for a macro to be able to invent different identifiers in different invocations. 

The GNU preprocessor is used for multiple languages, and is also exposed 
as an independent utility that can be used to process anything that has 
a sufficiently C-like token structure. 

Since local labels are intended for macros, they are not subject to 
diagnosis by -Wshadow.  In the ordinary namespace for 
variables/functions/typedefs, there is no such concession. Macros 
that reuse identifiers in nested scopes will trigger nuisance warnings 
from -Wshadow.
diff mbox series

Patch

diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index 90b2767e39a..d52450958d7 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -1941,6 +1941,87 @@  generate unique identifiers.  Care must be taken to 
ensure that
  @code{__COUNTER__} is not expanded prior to inclusion of precompiled headers
  which use it.  Otherwise, the precompiled headers will not be used.

+@item __EXP_COUNTER__
+This macro's name means "(macro) expansion counter".
+Outside of macro replacement sequences, it expands to the integer
+token @code{1}.  This make it possible to easily test for the presence
+of this feature using conditional directives such as
+@code{#if __EXP_COUNTER__}.
+When @code{__EXP_COUNTER__} occurs in the replacement token sequence
+of a macro, it expands to positive decimal integer token which
+uniquely identifies the expansion, within a translation unit.
+Unlike @code{__COUNTER__}, @code{__EXP_COUNTER__} does not increment
+on each access: all references to it which occur in the same token
+replacement sequence of the same instance of a macro being expanded
+produce the same integer token.
+
+The implementation of this feature works as follows: a counter is 
initialized
+to zero prior to the processing of any tokens of the translation unit.  
Whenever
+a macro is about to be expanded, the counter is incremented, and the 
counter's
+value is associated with that macro expansion context.  Subsequent 
increments of
+the counter, such as during rescanning of a token sequence for more macros, 
do
+not affect the captured value.  Nested macro expansions are associated with
+their own @code{__EXP_COUNTER__} values.  The counter uses the
+@code{unsigned long long} type of the host implementation for which the
+preprocessor is compiled.  If this counter overflows and 
@code{__EXP_COUNTER__}
+is subsequently accessed, the behavior is unspecified.
+
+The main use for @code{__EXP_COUNTER__} is the generation of unique symbols,
+as in the following example:
+
+@smallexample
+#define cat(a, b) a ## b
+#define xcat(a, b) cat (a, b)
+#define uni(pfx, count) xcat (pfx, xcat (_uniq_, count))
+
+#define repeat(count)                                       \
+  for (int uni (i, __EXP_COUNTER__) = 0,                    \
+       uni (c, __EXP_COUNTER__) = (count);                  \
+       uni (i, __EXP_COUNTER__) < uni (c, __EXP_COUNTER__); \
+       uni (i, __EXP_COUNTER__)++)
+
+repeat (get_repeat_count ())
+  puts ("Hello, world!")
+@end smallexample
+
+@item __UEXP_COUNTER__
+This macro is closely related to @code{__EXP_COUNTER__}.  In the
+expansion of a macro which is being expanded in the middle of another
+macro expansion, @code{__UEXP_COUNTER__} ("upper context expansion counter")
+produces the @code{__EXP_COUNTER__} value of the parent expansion.
+In any other situation, this macro expands to @code{1}.
+
+Consider the following example:
+
+@smallexample
+#define child __UEXP_COUNTER__ __EXP_COUNTER__
+#define parent @{ __EXP_COUNTER__ child @}
+parent
+@end smallexample
+
+Here, @code{parent} will expand to a sequence similar to @code{@{ 42 42 43 
@}}.
+The @code{__UEXP_COUNTER__} value from @code{child} matches the
+@code{__EXP_COUNTER__} in @code{parent}, but the @code{child}'s own
+@code{__EXP_COUNTER__} is, of course, different.
+
+The main use for @code{__UEXP_COUNTER__} is the simplification of macros
+that require @code{__EXP_COUNTER__}.  The example given for
+@code{__EXP_COUNTER__} can be simplified like this:
+
+@smallexample
+#define cat(a, b) a ## b
+#define xcat(a, b) cat (a, b)
+#define uni(pfx) xcat (pfx, xcat (_uniq_, __UEXP_COUNTER__))
+
+#define repeat(count)                       \
+  for (int uni (i) = 0, uni (c) = (count);  \
+       uni (i) < uni (c);                   \
+       uni (i)++)
+
+repeat (get_repeat_count ())
+  puts ("Hello, world!")
+@end smallexample
+
  @item __GFORTRAN__
  The GNU Fortran compiler defines this.

diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e147f69c8eb..fee0ae0ad4b 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,11 @@ 
+2022-04-21  Kaz Kylheku  <kaz@kylheku.com>
+
+	* gcc.dg/cpp/expcounter1.c: New test.
+	* gcc.dg/cpp/expcounter2.c: New test.
+	* gcc.dg/cpp/expcounter3.c: New test.
+	* gcc.dg/cpp/expcounter4.c: New test.
+	* gcc.dg/cpp/expcounter5.c: New test.
+
  2022-04-15  Paul A. Clarke  <pc@us.ibm.com>

  	* g++.dg/debug/dwarf2/const2.C: Move to g++.target/powerpc.
diff --git a/gcc/testsuite/gcc.dg/cpp/expcounter1.c 
b/gcc/testsuite/gcc.dg/cpp/expcounter1.c
new file mode 100644
index 00000000000..21bb89c7d13
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/expcounter1.c
@@ -0,0 +1,16 @@ 
+/* { dg-do preprocess } */
+/* { dg-options "-Wcpp" } */
+#if __EXP_COUNTER__ == 1
+#warning "yes" // { dg-warning "-:yes" }
+#endif
+#if __UEXP_COUNTER__ == 1
+#warning "yes" // { dg-warning "-:yes" }
+#endif
+#define __EXP_COUNTER__ 42 // { dg-warning "-:redefined" }
+#define __UEXP_COUNTER__ 73 // { dg-warning "-:redefined" }
+#if __EXP_COUNTER__ == 42
+#warning "yes" // { dg-warning "-:yes" }
+#endif
+#if __UEXP_COUNTER__ == 73
+#warning "yes" // { dg-warning "-:yes" }
+#endif
diff --git a/gcc/testsuite/gcc.dg/cpp/expcounter2.c 
b/gcc/testsuite/gcc.dg/cpp/expcounter2.c
new file mode 100644
index 00000000000..de1cf6fca45
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/expcounter2.c
@@ -0,0 +1,21 @@ 
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#define M0(X) X, __EXP_COUNTER__
+#define M1(X) X, M0(__EXP_COUNTER__), __EXP_COUNTER__
+#define M2(X) X, M1(__EXP_COUNTER__), __EXP_COUNTER__
+
+int out[] = { M2(__EXP_COUNTER__), __EXP_COUNTER__ };
+int ref[] = { 1, 3, 4, 5, 4, 3, 1 };
+
+#include <string.h>
+#include <stdlib.h>
+
+int main()
+{
+  if (sizeof(out) != sizeof(ref))
+    return EXIT_FAILURE;
+  if (memcmp(out, ref, sizeof out) != 0)
+    return EXIT_FAILURE;
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/cpp/expcounter3.c 
b/gcc/testsuite/gcc.dg/cpp/expcounter3.c
new file mode 100644
index 00000000000..b7f9bb03a7f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/expcounter3.c
@@ -0,0 +1,22 @@ 
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#define M0(X) X, __UEXP_COUNTER__
+#define M1(X) X, M0(__UEXP_COUNTER__), __UEXP_COUNTER__
+#define M2(X) X, M1(__UEXP_COUNTER__), __UEXP_COUNTER__
+#define M3(X) X, M2(__UEXP_COUNTER__), __UEXP_COUNTER__
+
+int out[] = { M3(__UEXP_COUNTER__), __UEXP_COUNTER__ };
+int ref[] = { 1, 1, 3, 4, 5, 4, 3, 1, 1 };
+
+#include <string.h>
+#include <stdlib.h>
+
+int main()
+{
+  if (sizeof(out) != sizeof(ref))
+    return EXIT_FAILURE;
+  if (memcmp(out, ref, sizeof out) != 0)
+    return EXIT_FAILURE;
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/cpp/expcounter4.c 
b/gcc/testsuite/gcc.dg/cpp/expcounter4.c
new file mode 100644
index 00000000000..47f0732a655
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/expcounter4.c
@@ -0,0 +1,22 @@ 
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#define M0 __UEXP_COUNTER__
+#define M1 M0, __EXP_COUNTER__, __UEXP_COUNTER__
+#define M2 M1, __EXP_COUNTER__, __UEXP_COUNTER__
+#define M3 M2, __EXP_COUNTER__, __UEXP_COUNTER__
+
+int out[] = { M3, __EXP_COUNTER__, __UEXP_COUNTER__ };
+int ref[] = { 5, 5, 4, 4, 3, 3, 1, 1, 1 };
+
+#include <string.h>
+#include <stdlib.h>
+
+int main()
+{
+  if (sizeof(out) != sizeof(ref))
+    return EXIT_FAILURE;
+  if (memcmp(out, ref, sizeof out) != 0)
+    return EXIT_FAILURE;
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/cpp/expcounter5.c 
b/gcc/testsuite/gcc.dg/cpp/expcounter5.c
new file mode 100644
index 00000000000..59b0e780768
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/expcounter5.c
@@ -0,0 +1,28 @@ 
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#define cat(a, b) a ## b
+#define xcat(a, b) cat(a, b)
+#define uni(pfx) xcat(pfx, xcat(_uniq_, __UEXP_COUNTER__))
+
+#define repeat(count)                       \
+  for (int uni(i) = 0, uni(c) = (count);    \
+       uni(i) < uni(c);                     \
+       uni(i)++)
+
+#define str(x) #x
+#define xstr(x) str(x)
+
+const char *out = xstr(repeat(42) repeat(73) foo(););
+const char *ref = "for (int i_uniq_3 = 0, c_uniq_3 = (42); "
+                  "i_uniq_3 < c_uniq_3; i_uniq_3++) "
+                  "for (int i_uniq_29 = 0, c_uniq_29 = (73); "
+                  "i_uniq_29 < c_uniq_29; i_uniq_29++) foo();";
+
+#include <string.h>
+#include <stdlib.h>
+
+int main()
+{
+  return (strcmp(out, ref) == 0) ? 0 : EXIT_FAILURE;
+}
diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog
index a0242895188..024924f9fe1 100644
--- a/libcpp/ChangeLog
+++ b/libcpp/ChangeLog
@@ -1,3 +1,52 @@ 
+2022-04-21  Kaz Kylheku  <kaz@kylheku.com>
+
+	This change introduces a pair of related macros
+	__EXP_COUNTER__ and __UEXP_COUNTER__.  These macros access
+	integer values which enumerate macro expansions.
+	They can be used for the purposes of obtaining, unique
+	identifiers (within the scope of a translation unit), as a
+	replacement for unreliable hacks based on __LINE__.
+
+	Outside of macro expansions, these macros epand to 1,
+	so they are easy to test for in portable code that needs
+	to fall back on something, like __LINE__.
+
+	* gcc/doc/cpp.texi (__EXP_COUNTER__, __UEXP_COUNTER__):
+	Special built-in macros documented.
+
+	* libcpp/include/cpplib.h (struct cpp_macro): New members of
+	type long long: exp_number and uexp_number.  These members are
+	used to attach the current exp_counter value, and the parent
+	context's copy of it to a new macro expansion.  The special
+	macros then just access these.
+	(enum cpp_builtin_type): New enumeration members,
+	BT_EXP_COUNTER and BT_UEXP_COUNTER.
+
+	* libcpp/macro.cc (exp_counter): New static variable for
+	counting expansions.  There is an existing one,
+	num_expanded_macros_counter, but that has its own purpose and
+	is incremented in a specific place.  Plus it uses a narrower
+	integer type.
+	(_cpp_builtin_number_text): Change the local variable "number"
+	from linenum_type to unsigned long long, so it holds at least
+	64 bit values.  Handle the BT_EXP_COUNTER and BT_UEXP_COUNTER
+	cases.  These just have to see if there is a current macro, and
+	retrieve the values from it, otherwise do nothing so that the
+	default 1 is produced.  In the case of BT_UEXP_COUNTER, if the
+	value is zero, we don't use it, so 1 emerges.  The sprintf of
+	the number is adjusted to use the right conversion specifier
+	for the wider type.  Space is already being reserved for
+	a 64 bit decimal.
+	(enter_macro_context): After processing the macro arguments,
+	if any, we increment exp_counter and attach its new value to
+	the macro's context structure in the exp_number member.  We
+	also calculate the macro's uexp_number: the parent context's
+	exp_number.  This is tricky: we have to chase the previous
+	macro context.  This works if the macro is object-like, or has
+	no parameters.  If it has parameters, there is a parameter
+	context, and so we have to climb one more flight of stairs to
+	get to the real context.
+
  2022-02-11  Joseph Myers  <joseph@codesourcery.com>

  	* Makefile.in (po/$(PACKAGE).pot): Also handle cpp_warning_at,
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 3eba6f74b57..f3c724a1a15 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -827,6 +827,12 @@  struct GTY(()) cpp_macro {
      cpp_macro *GTY ((tag ("true"))) next;
    } GTY ((desc ("%1.kind == cmk_assert"))) parm;

+  /* Global expansion number, derived from exp_counter.  */
+  long long exp_number;
+
+  /* Parent expansion number; zero if unavailable.  */
+  long long uexp_number;
+
    /* Definition line number.  */
    location_t line;

@@ -920,6 +926,8 @@  enum cpp_builtin_type
    BT_PRAGMA,			/* `_Pragma' operator */
    BT_TIMESTAMP,			/* `__TIMESTAMP__' */
    BT_COUNTER,			/* `__COUNTER__' */
+  BT_EXP_COUNTER,		/* `__EXP_COUNTER__' */
+  BT_UEXP_COUNTER,		/* `__UEXP_COUNTER__' */
    BT_HAS_ATTRIBUTE,		/* `__has_attribute(x)' */
    BT_HAS_STD_ATTRIBUTE,		/* `__has_c_attribute(x)' */
    BT_HAS_BUILTIN,		/* `__has_builtin(x)' */
diff --git a/libcpp/init.cc b/libcpp/init.cc
index f4ab83d2145..60b0e482412 100644
--- a/libcpp/init.cc
+++ b/libcpp/init.cc
@@ -411,6 +411,8 @@  static const struct builtin_macro builtin_array[] =
    B("__LINE__",		 BT_SPECLINE,      true),
    B("__INCLUDE_LEVEL__", BT_INCLUDE_LEVEL, true),
    B("__COUNTER__",	 BT_COUNTER,       true),
+  B("__EXP_COUNTER__",	 BT_EXP_COUNTER,   true),
+  B("__UEXP_COUNTER__",	 BT_UEXP_COUNTER,  true),
    /* Make sure to update the list of built-in
       function-like macros in traditional.cc:
       fun_like_macro() when adding more following */
diff --git a/libcpp/macro.cc b/libcpp/macro.cc
index 8ebf360c03c..5a14a655c0f 100644
--- a/libcpp/macro.cc
+++ b/libcpp/macro.cc
@@ -362,6 +362,10 @@  static const cpp_token* cpp_get_token_1 (cpp_reader *, 
location_t *);

  static cpp_hashnode* macro_of_context (cpp_context *context);

+/* Counter tracking the number of macros expanded, for purposes
+   of __EXP_COUNTER__.  */
+static unsigned long exp_counter = 0;
+
  /* Statistical counter tracking the number of macros that got
     expanded.  */