libcpp/ChangeLog
2022-04-21  Kaz Kylheku  <k...@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  <k...@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 <k...@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__}.
+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  <k...@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  <p...@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  <k...@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  <jos...@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.  */
 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);
--
2.17.1

Reply via email to