From b24d8acbfffe30f40e280f11f23adac81b1e7f0c Mon Sep 17 00:00:00 2001 From: Joseph Myers Date: Thu, 29 Apr 2021 19:50:47 +0000 Subject: preprocessor: Handle digit separators in #line [PR82359] As reported in bug 82359, the preprocessor does not allow C++ digit separators in the line number in a #line directive, despite the standard syntax for that directive using digit-sequence which allows digit separators. There is some confusion in that bug about whether C++ is meant to allow digit separators there or not, but the last comment there suggests they are meant to be allowed, and the version of digit separators accepted for C2X at the March meeting explicitly mentions digit separators in the #line specification to avoid any ambiguity there. This patch thus adds code to handle digit separators in the line number in #line, as part of the preparation for enabling digit separators in C2X mode. The code changed does not contain any conditionals for whether digit separators are supported in the chosen language version, because that was handled earlier in pp-number lexing and if they aren't supported they won't appear in the string passed to that function. It does however make sure not to allow adjacent digit separators because those are only handled at a later stage of lexing at present. (Problems with how certain source character sequences involving digit separators that don't actually match the pp-number syntax get lexed as a pp-number and only diagnosed later, if at all, are bugs 83873 and 97604, to be addressed separately.) Making the change in this location will have the effect of allowing digit separators in the "# " form of directive as well as #line; I don't think that's a problem. Bootstrapped with no regressions for x86_64-pc-linux-gnu. libcpp/ PR preprocessor/82359 * directives.c (strtolinenum): Handle digit separators. gcc/testsuite/ PR preprocessor/82359 * g++.dg/cpp1y/digit-sep-line.C, g++.dg/cpp1y/digit-sep-line-neg.C: New tests. --- libcpp/directives.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'libcpp/directives.c') diff --git a/libcpp/directives.c b/libcpp/directives.c index f4aa17d..795f93e 100644 --- a/libcpp/directives.c +++ b/libcpp/directives.c @@ -922,12 +922,19 @@ strtolinenum (const uchar *str, size_t len, linenum_type *nump, bool *wrapped) linenum_type reg = 0; uchar c; + bool seen_digit_sep = false; *wrapped = false; while (len--) { c = *str++; + if (!seen_digit_sep && c == '\'' && len) + { + seen_digit_sep = true; + continue; + } if (!ISDIGIT (c)) return true; + seen_digit_sep = false; if (reg > ((linenum_type) -1) / 10) *wrapped = true; reg *= 10; -- cgit v1.1 From 71d38ec80008afdbb9a059253407d80598b765c0 Mon Sep 17 00:00:00 2001 From: Joseph Myers Date: Tue, 11 May 2021 23:54:01 +0000 Subject: preprocessor: Support C2X #elifdef, #elifndef C2X adds #elifdef and #elifndef preprocessor directives; these have also been proposed for C++. Implement these directives in libcpp accordingly. In this implementation, #elifdef and #elifndef are treated as non-directives for any language version other than c2x and gnu2x (if the feature is accepted for C++, it can trivially be enabled for relevant C++ versions). In strict conformance modes for prior language versions, this is required, as illustrated by the c11-elifdef-1.c test added. Bootstrapped with no regressions for x86_64-pc-linux-gnu. libcpp/ * include/cpplib.h (struct cpp_options): Add elifdef. * init.c (struct lang_flags): Add elifdef. (lang_defaults): Update to include elifdef initializers. (cpp_set_lang): Set elifdef for pfile based on language. * directives.c (STDC2X, ELIFDEF): New macros. (EXTENSION): Increase value to 3. (DIRECTIVE_TABLE): Add #elifdef and #elifndef. (_cpp_handle_directive): Do not treat ELIFDEF directives as directives for language versions without the #elifdef feature. (do_elif): Handle #elifdef and #elifndef. (do_elifdef, do_elifndef): New functions. gcc/testsuite/ * gcc.dg/cpp/c11-elifdef-1.c, gcc.dg/cpp/c2x-elifdef-1.c, gcc.dg/cpp/c2x-elifdef-2.c: New tests. --- libcpp/directives.c | 68 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 10 deletions(-) (limited to 'libcpp/directives.c') diff --git a/libcpp/directives.c b/libcpp/directives.c index 795f93e..261a584 100644 --- a/libcpp/directives.c +++ b/libcpp/directives.c @@ -56,10 +56,12 @@ struct pragma_entry /* Values for the origin field of struct directive. KANDR directives come from traditional (K&R) C. STDC89 directives come from the - 1989 C standard. EXTENSION directives are extensions. */ + 1989 C standard. STDC2X directives come from the C2X standard. EXTENSION + directives are extensions. */ #define KANDR 0 #define STDC89 1 -#define EXTENSION 2 +#define STDC2X 2 +#define EXTENSION 3 /* Values for the flags field of struct directive. COND indicates a conditional; IF_COND an opening conditional. INCL means to treat @@ -67,13 +69,17 @@ struct pragma_entry means this directive should be handled even if -fpreprocessed is in effect (these are the directives with callback hooks). - EXPAND is set on directives that are always macro-expanded. */ + EXPAND is set on directives that are always macro-expanded. + + ELIFDEF is set on directives that are only handled for standards with the + #elifdef / #elifndef feature. */ #define COND (1 << 0) #define IF_COND (1 << 1) #define INCL (1 << 2) #define IN_I (1 << 3) #define EXPAND (1 << 4) #define DEPRECATED (1 << 5) +#define ELIFDEF (1 << 6) /* Defines one #-directive, including how to handle it. */ typedef void (*directive_handler) (cpp_reader *); @@ -148,6 +154,8 @@ static void cpp_pop_definition (cpp_reader *, struct def_pragma_macro *); D(undef, T_UNDEF, KANDR, IN_I) \ D(line, T_LINE, KANDR, EXPAND) \ D(elif, T_ELIF, STDC89, COND | EXPAND) \ + D(elifdef, T_ELIFDEF, STDC2X, COND | ELIFDEF) \ + D(elifndef, T_ELIFNDEF, STDC2X, COND | ELIFDEF) \ D(error, T_ERROR, STDC89, 0) \ D(pragma, T_PRAGMA, STDC89, IN_I) \ D(warning, T_WARNING, EXTENSION, 0) \ @@ -437,7 +445,11 @@ _cpp_handle_directive (cpp_reader *pfile, bool indented) if (dname->type == CPP_NAME) { if (dname->val.node.node->is_directive) - dir = &dtable[dname->val.node.node->directive_index]; + { + dir = &dtable[dname->val.node.node->directive_index]; + if ((dir->flags & ELIFDEF) && !CPP_OPTION (pfile, elifdef)) + dir = 0; + } } /* We do not recognize the # followed by a number extension in assembler code. */ @@ -2079,8 +2091,8 @@ do_else (cpp_reader *pfile) } } -/* Handle a #elif directive by not changing if_stack either. See the - comment above do_else. */ +/* Handle a #elif, #elifdef or #elifndef directive by not changing if_stack + either. See the comment above do_else. */ static void do_elif (cpp_reader *pfile) { @@ -2088,12 +2100,13 @@ do_elif (cpp_reader *pfile) struct if_stack *ifs = buffer->if_stack; if (ifs == NULL) - cpp_error (pfile, CPP_DL_ERROR, "#elif without #if"); + cpp_error (pfile, CPP_DL_ERROR, "#%s without #if", pfile->directive->name); else { if (ifs->type == T_ELSE) { - cpp_error (pfile, CPP_DL_ERROR, "#elif after #else"); + cpp_error (pfile, CPP_DL_ERROR, "#%s after #else", + pfile->directive->name); cpp_error_with_line (pfile, CPP_DL_ERROR, ifs->line, 0, "the conditional began here"); } @@ -2107,8 +2120,29 @@ do_elif (cpp_reader *pfile) pfile->state.skipping = 1; else { - pfile->state.skipping = ! _cpp_parse_expr (pfile, false); - ifs->skip_elses = ! pfile->state.skipping; + if (pfile->directive == &dtable[T_ELIF]) + pfile->state.skipping = !_cpp_parse_expr (pfile, false); + else + { + cpp_hashnode *node = lex_macro_node (pfile, false); + + if (node) + { + bool macro_defined = _cpp_defined_macro_p (node); + if (!_cpp_maybe_notify_macro_use (pfile, node, + pfile->directive_line)) + /* It wasn't a macro after all. */ + macro_defined = false; + bool skip = (pfile->directive == &dtable[T_ELIFDEF] + ? !macro_defined + : macro_defined); + if (pfile->cb.used) + pfile->cb.used (pfile, pfile->directive_line, node); + check_eol (pfile, false); + pfile->state.skipping = skip; + } + } + ifs->skip_elses = !pfile->state.skipping; } /* Invalidate any controlling macro. */ @@ -2116,6 +2150,20 @@ do_elif (cpp_reader *pfile) } } +/* Handle a #elifdef directive. */ +static void +do_elifdef (cpp_reader *pfile) +{ + do_elif (pfile); +} + +/* Handle a #elifndef directive. */ +static void +do_elifndef (cpp_reader *pfile) +{ + do_elif (pfile); +} + /* #endif pops the if stack and resets pfile->state.skipping. */ static void do_endif (cpp_reader *pfile) -- cgit v1.1