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