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