aboutsummaryrefslogtreecommitdiff
path: root/libcpp/internal.h
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2025-07-30 13:20:59 +0200
committerJakub Jelinek <jakub@gcc.gnu.org>2025-07-30 13:20:59 +0200
commit8f185d3d7a2bcbbfb1a8f70ac602ee6e4ac34080 (patch)
tree15e2561bbd39060c63c1d524cebe2f2e24dd9af9 /libcpp/internal.h
parent28310b3ff84616cea242cb4cef8f16b4538dbd22 (diff)
downloadgcc-8f185d3d7a2bcbbfb1a8f70ac602ee6e4ac34080.zip
gcc-8f185d3d7a2bcbbfb1a8f70ac602ee6e4ac34080.tar.gz
gcc-8f185d3d7a2bcbbfb1a8f70ac602ee6e4ac34080.tar.bz2
libcpp: Fix up comma diagnostics in preprocessor for C++ [PR120778]
The P2843R3 Preprocessing is never undefined paper contains comments that various compilers handle comma operators in preprocessor expressions incorrectly and I think they are right. In both C and C++ the grammar uses constant-expression non-terminal for #if/#elif and in both C and C++ that NT is conditional-expression, so #if 1, 2 is IMHO clearly wrong in both languages. C89 then says for constant-expression "Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within the operand of a sizeof operator." Because all the remaining identifiers in the #if/#elif expression are replaced with 0 I think assignments, increment, decrement and function-call aren't that big deal because (0 = 1) or ++4 etc. are all invalid, but for comma expressions I think it matters. In r0-56429 PR456 Joseph has added !CPP_OPTION (pfile, c99) to handle that correctly. Then C99 changed that to: "Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated." That made for C99+ #if 1 || (1, 2) etc. valid but #if (1, 2) is still invalid, ditto #if 1 ? 1, 2 : 3 In C++ I can't find anything like that though, and as can be seen on say int a[(1, 2)]; int b[1 ? 1, 2 : 3]; being accepted by C++ and rejected by C while int c[1, 2]; int d[1 ? 2 : 3, 4]; being rejected in both C and C++, so I think for C++ it is indeed just the grammar that prevents #if 1, 2. When it is the second operand of ?: or inside of () the grammar just uses expression and that allows comma operator. So, the following patch uses different decisions for C++ when to diagnose comma operator in preprocessor expressions, for C++ tracks if it is inside of () (obviously () around #embed clauses don't count unless one uses limit ((1, 2)) etc.) or inside of the second ?: operand and allows comma operator there and disallows elsewhere. BTW, I wonder if anything in the standard disallows <=> in the preprocessor expressions. Say #if (0 <=> 1) < 0 etc. #include <compare> constexpr int a = (0 <=> 1) < 0; is valid (but not valid without #include <compare>) and the expressions don't use any identifiers. 2025-07-30 Jakub Jelinek <jakub@redhat.com> PR c++/120778 * internal.h (struct lexer_state): Add comma_ok member. * expr.cc (_cpp_parse_expr): Initialize it to 0, increment on CPP_OPEN_PAREN and CPP_QUERY, decrement on CPP_CLOSE_PAREN and CPP_COLON. (num_binary_op): For C++ pedwarn on comma operator if pfile->state.comma_ok is 0 instead of !c99 or skip_eval. * g++.dg/cpp/if-comma-1.C: New test.
Diffstat (limited to 'libcpp/internal.h')
-rw-r--r--libcpp/internal.h3
1 files changed, 3 insertions, 0 deletions
diff --git a/libcpp/internal.h b/libcpp/internal.h
index 8ca4c75..bcf5559 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -282,6 +282,9 @@ struct lexer_state
/* Nonzero to skip evaluating part of an expression. */
unsigned int skip_eval;
+ /* Nonzero if CPP_COMMA is valid in expression in C++. */
+ unsigned int comma_ok;
+
/* Nonzero when tokenizing a deferred pragma. */
unsigned char in_deferred_pragma;