On 2024-03-20 16:34, [email protected] wrote:
> On 19 March 2024 18:27:13 CET, Kaz Kylheku <[email protected]> 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 <[email protected]>
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 <[email protected]>
---
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