On 2024-03-20 16:34, rep.dot....@gmail.com wrote:
> On 19 March 2024 18:27:13 CET, Kaz Kylheku <k...@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;
}
From 4aded10c4171f9a9a361bb8986d721357f1fc2c8 Mon Sep 17 00:00:00 2001
From: Kaz Kylheku <k...@kylheku.com>
Date: Wed, 20 Apr 2022 01:15:24 -0700
Subject: [PATCH] cpp: new built-in __EXPANSION_NUMBER__

This change introduces a pair of related macros __EXPANSION_NUMBER__ and
__PARENT_EXPANSION_NUMBER__.  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 another
approach, perhaps involving __LINE__.

libcpp/ChangeLog:

	* libcpp/include/cpplib.h (struct cpp_macro): New members of
	type long long: expansion_number and parent_expansion_number.
	These members are used to attach the current expansion_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_EXPANSION_NUMBER and BT_PARENT_EXPANSION_NUMBER.

	* libcpp/macro.cc (expansion_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_EXPANSION_NUMBER and
	BT_PARENT_EXPANSION_NUMBER 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_PARENT_EXPANSION_NUMBER, 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 expansion_counter and attach its new value to
	the macro's context structure in the expansion_number member.
	We also calculate the emacro's parent_expansion_number: the
	parent context's exp_cnumber.  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/ChangeLog:

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

gcc/testsuite/ChangeLog:

	* 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 <k...@kylheku.com>
---
 gcc/doc/cpp.texi                       | 84 ++++++++++++++++++++++++++
 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/include/cpplib.h                |  8 +++
 libcpp/init.cc                         |  2 +
 libcpp/macro.cc                        | 44 +++++++++++++-
 9 files changed, 245 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 3de6e7aa737..40aa6c31375 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -1942,6 +1942,90 @@ 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 __EXPANSION_NUMBER__
+This macro's name means "(macro) expansion number".  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{#ifdef __EXPANSION_NUMBER__}.
+When @code{__EXPANSION_NUMBER__} occurs in the replacement token
+sequence of a macro, it expands to a positive decimal integer token
+which uniquely identifies the expansion, within a translation unit.
+Unlike @code{__COUNTER__}, @code{__EXPANSION_NUMBER__} 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{__EXPANSION_NUMBER__} 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{__EXPANSION_NUMBER__} is subsequently accessed, the behavior is
+unspecified.
+
+The main use for @code{__EXPANSION_NUMBER__} 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, __EXPANSION_NUMBER__) = 0,                         \
+       uni (c, __EXPANSION_NUMBER__) = (count);                       \
+       uni (i, __EXPANSION_NUMBER__) < uni (c, __EXPANSION_NUMBER__); \
+       uni (i, __EXPANSION_NUMBER__)++)
+
+repeat (get_repeat_count ())
+  puts ("Hello, world!")
+@end smallexample
+
+@item __PARENT_EXPANSION_NUMBER__
+This macro is closely related to @code{__EXPANSION_NUMBER__}.  In the
+expansion of a macro which is being expanded in the middle of another
+macro expansion, @code{__PARENT_EXPANSION_NUMBER__} produces the
+@code{__EXPANSION_NUMBER__} value of the parent expansion.  In any other
+situation, this macro expands to @code{1}.
+
+Consider the following example:
+
+@smallexample
+#define child __PARENT_EXPANSION_NUMBER__ __EXPANSION_NUMBER__
+#define parent @{ __EXPANSION_NUMBER__ child @}
+parent
+@end smallexample
+
+Here, @code{parent} will expand to a sequence similar to
+@code{@{ 42 42 43 @}}.  The @code{__PARENT_EXPANSION_NUMBER__} value
+from @code{child} matches the @code{__EXPANSION_NUMBER__} in
+@code{parent}, but the @code{child}'s own @code{__EXPANSION_NUMBER__}
+is, of course, different.
+
+The main use for @code{__PARENT_EXPANSION_NUMBER__} is the
+simplification of macros that require @code{__EXPANSION_NUMBER__}.  The
+example given for @code{__EXPANSION_NUMBER__} 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_, __PARENT_EXPANSION_NUMBER__))
+
+#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/gcc.dg/cpp/expcounter1.c b/gcc/testsuite/gcc.dg/cpp/expcounter1.c
new file mode 100644
index 00000000000..37c2e4626d3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/expcounter1.c
@@ -0,0 +1,16 @@
+/* { dg-do preprocess } */
+/* { dg-options "-Wcpp" } */
+#if __EXPANSION_NUMBER__ == 1
+#warning "yes" // { dg-warning "-:yes" }
+#endif
+#if __PARENT_EXPANSION_NUMBER__ == 1
+#warning "yes" // { dg-warning "-:yes" }
+#endif
+#define __EXPANSION_NUMBER__ 42 // { dg-warning "-:\"__EXPANSION_NUMBER__\" redefined" }
+#define __PARENT_EXPANSION_NUMBER__ 73 // { dg-warning "-:\"__PARENT_EXPANSION_NUMBER__\" redefined" }
+#if __EXPANSION_NUMBER__ == 42
+#warning "yes" // { dg-warning "-:yes" }
+#endif
+#if __PARENT_EXPANSION_NUMBER__ == 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..7b20631ca5d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/expcounter2.c
@@ -0,0 +1,21 @@
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#define M0(X) X, __EXPANSION_NUMBER__
+#define M1(X) X, M0(__EXPANSION_NUMBER__), __EXPANSION_NUMBER__
+#define M2(X) X, M1(__EXPANSION_NUMBER__), __EXPANSION_NUMBER__
+
+int out[] = { M2(__EXPANSION_NUMBER__), __EXPANSION_NUMBER__ };
+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..8c27e0754c2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/expcounter3.c
@@ -0,0 +1,22 @@
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#define M0(X) X, __PARENT_EXPANSION_NUMBER__
+#define M1(X) X, M0(__PARENT_EXPANSION_NUMBER__), __PARENT_EXPANSION_NUMBER__
+#define M2(X) X, M1(__PARENT_EXPANSION_NUMBER__), __PARENT_EXPANSION_NUMBER__
+#define M3(X) X, M2(__PARENT_EXPANSION_NUMBER__), __PARENT_EXPANSION_NUMBER__
+
+int out[] = { M3(__PARENT_EXPANSION_NUMBER__), __PARENT_EXPANSION_NUMBER__ };
+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..6538b701876
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/expcounter4.c
@@ -0,0 +1,22 @@
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#define M0 __PARENT_EXPANSION_NUMBER__
+#define M1 M0, __EXPANSION_NUMBER__, __PARENT_EXPANSION_NUMBER__
+#define M2 M1, __EXPANSION_NUMBER__, __PARENT_EXPANSION_NUMBER__
+#define M3 M2, __EXPANSION_NUMBER__, __PARENT_EXPANSION_NUMBER__
+
+int out[] = { M3, __EXPANSION_NUMBER__, __PARENT_EXPANSION_NUMBER__ };
+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..a09406ce91a
--- /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_, __PARENT_EXPANSION_NUMBER__))
+
+#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/include/cpplib.h b/libcpp/include/cpplib.h
index c62374d3192..637b85f2d5a 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -875,6 +875,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 expansion_number;
+
+  /* Parent expansion number; zero if unavailable.  */
+  long long parent_expansion_number;
+
   /* Definition line number.  */
   location_t line;
 
@@ -968,6 +974,8 @@ enum cpp_builtin_type
   BT_PRAGMA,			/* `_Pragma' operator */
   BT_TIMESTAMP,			/* `__TIMESTAMP__' */
   BT_COUNTER,			/* `__COUNTER__' */
+  BT_EXPANSION_NUMBER,		/* `__EXPANSION_NUMBER__' */
+  BT_PARENT_EXPANSION_NUMBER,	/* `__PARENT_EXPANSION_NUMBER__' */
   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 54fc9236d38..62c71b0518e 100644
--- a/libcpp/init.cc
+++ b/libcpp/init.cc
@@ -426,6 +426,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("__EXPANSION_NUMBER__",	 	BT_EXPANSION_NUMBER,   true),
+  B("__PARENT_EXPANSION_NUMBER__",	BT_PARENT_EXPANSION_NUMBER,  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 352eb2e4fd9..ef0b74e9434 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 __EXPANSION_NUMBER__.  */
+static unsigned long expansion_counter = 0;
+
 /* Statistical counter tracking the number of macros that got
    expanded.  */
 unsigned num_expanded_macros_counter = 0;
@@ -493,7 +497,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)
     {
@@ -663,6 +667,26 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node,
       number = pfile->counter++;
       break;
 
+    case BT_EXPANSION_NUMBER:
+      {
+	cpp_hashnode *macro = macro_of_context (pfile->context);
+	if (macro != NULL)
+	  number = macro->value.macro->expansion_number;
+      }
+      break;
+
+    case BT_PARENT_EXPANSION_NUMBER:
+      {
+	cpp_hashnode *macro = macro_of_context (pfile->context);
+	if (macro != NULL)
+	  {
+	    unsigned long long ue = macro->value.macro->parent_expansion_number;
+	    if (ue > 0)
+	      number = ue;
+	  }
+      }
+      break;
+
     case BT_HAS_ATTRIBUTE:
       number = pfile->cb.has_attribute (pfile, false);
       break;
@@ -692,7 +716,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;      
@@ -1501,6 +1525,22 @@ enter_macro_context (cpp_reader *pfile, cpp_hashnode *node,
       /* Disable the macro within its expansion.  */
       node->flags |= NODE_DISABLED;
 
+      /* Increment and capture __EXPANSION_NUMBER__ counter.  */
+      macro->expansion_number = ++expansion_counter;
+
+      /* If we are in the middle of an existing macro, get *its*
+	 expansion_number into parent_expansion_number, for __PARENT_EXPANSION_NUMBER__. */
+      {
+	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->parent_expansion_number = existing_macro->value.macro->expansion_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);
-- 
2.17.1

Reply via email to