Hi! __has_{cpp_,}attribute builtin macros are effectively function-like macros taking one argument (and the ISO preprocessor expands macros in the argument which is IMHO desirable), but the traditional preprocessor has been crashing on them or reporting errors. As the hook uses cpp_get_token and thus the ISO preprocessor, we need to set up things such that the argument and ()s around it are already preprocessed and ready to be reparsed by the ISO preprocessor (this is similar to how e.g. #if/#elif and various other directives are handled).
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2015-03-11 Jakub Jelinek <ja...@redhat.com> PR preprocessor/65238 * internal.h (_cpp_scan_out_logical_line): Add third argument. * directives.c (prepare_directive_trad): Pass false to it. * traditional.c (_cpp_read_logical_line_trad, _cpp_create_trad_definition): Likewise. (struct fun_macro): Add paramc field. (fun_like_macro): New function. (maybe_start_funlike): Handle NODE_BUILTIN macros. Initialize macro->paramc field. (save_argument): Use macro->paramc instead of macro->node->value.macro->paramc. (push_replacement_text): Formatting fix. (recursive_macro): Use fun_like_macro helper. (_cpp_scan_out_logical_line): Likewise. Add BUILTIN_MACRO_ARG argument. Initialize fmacro.paramc field. Handle builtin function-like macros. * c-c++-common/cpp/pr65238-1.c: New test. * gcc.dg/cpp/pr65238-2.c: New test. * gcc.dg/cpp/trad/pr65238-3.c: New test. * gcc.dg/cpp/trad/pr65238-4.c: New test. --- libcpp/internal.h.jj 2015-02-03 10:33:59.000000000 +0100 +++ libcpp/internal.h 2015-03-11 14:11:13.410854083 +0100 @@ -708,7 +708,7 @@ extern void _cpp_preprocess_dir_only (cp const struct _cpp_dir_only_callbacks *); /* In traditional.c. */ -extern bool _cpp_scan_out_logical_line (cpp_reader *, cpp_macro *); +extern bool _cpp_scan_out_logical_line (cpp_reader *, cpp_macro *, bool); extern bool _cpp_read_logical_line_trad (cpp_reader *); extern void _cpp_overlay_buffer (cpp_reader *pfile, const unsigned char *, size_t); --- libcpp/directives.c.jj 2015-01-23 19:18:20.000000000 +0100 +++ libcpp/directives.c 2015-03-11 14:11:34.742511193 +0100 @@ -346,7 +346,7 @@ prepare_directive_trad (cpp_reader *pfil if (no_expand) pfile->state.prevent_expansion++; - _cpp_scan_out_logical_line (pfile, NULL); + _cpp_scan_out_logical_line (pfile, NULL, false); if (no_expand) pfile->state.prevent_expansion--; --- libcpp/traditional.c.jj 2015-03-10 16:37:11.418949471 +0100 +++ libcpp/traditional.c 2015-03-11 16:19:05.598475497 +0100 @@ -62,6 +62,9 @@ struct fun_macro /* The line the macro name appeared on. */ source_location line; + /* Number of parameters. */ + unsigned int paramc; + /* Zero-based index of argument being currently lexed. */ unsigned int argc; }; @@ -304,24 +307,41 @@ _cpp_read_logical_line_trad (cpp_reader if (pfile->buffer->need_line && !_cpp_get_fresh_line (pfile)) return false; } - while (!_cpp_scan_out_logical_line (pfile, NULL) || pfile->state.skipping); + while (!_cpp_scan_out_logical_line (pfile, NULL, false) + || pfile->state.skipping); return pfile->buffer != NULL; } +/* Return true if NODE is a fun_like macro. */ +static inline bool +fun_like_macro (cpp_hashnode *node) +{ + if (node->flags & NODE_BUILTIN) + return node->value.builtin == BT_HAS_ATTRIBUTE; + else + return node->value.macro->fun_like; +} + /* Set up state for finding the opening '(' of a function-like macro. */ static void -maybe_start_funlike (cpp_reader *pfile, cpp_hashnode *node, const uchar *start, struct fun_macro *macro) +maybe_start_funlike (cpp_reader *pfile, cpp_hashnode *node, const uchar *start, + struct fun_macro *macro) { - unsigned int n = node->value.macro->paramc + 1; + unsigned int n; + if (node->flags & NODE_BUILTIN) + n = 1; + else + n = node->value.macro->paramc; if (macro->buff) _cpp_release_buff (pfile, macro->buff); - macro->buff = _cpp_get_buff (pfile, n * sizeof (size_t)); + macro->buff = _cpp_get_buff (pfile, (n + 1) * sizeof (size_t)); macro->args = (size_t *) BUFF_FRONT (macro->buff); macro->node = node; macro->offset = start - pfile->out.base; + macro->paramc = n; macro->argc = 0; } @@ -330,7 +350,7 @@ static void save_argument (struct fun_macro *macro, size_t offset) { macro->argc++; - if (macro->argc <= macro->node->value.macro->paramc) + if (macro->argc <= macro->paramc) macro->args[macro->argc] = offset; } @@ -340,9 +360,13 @@ save_argument (struct fun_macro *macro, If MACRO is non-NULL, then we are scanning the replacement list of MACRO, and we call save_replacement_text() every time we meet an - argument. */ + argument. + + If BUILTIN_MACRO_ARG is true, this is called to macro expand + arguments of builtin function-like macros. */ bool -_cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro) +_cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro, + bool builtin_macro_arg) { bool result = true; cpp_context *context; @@ -359,14 +383,18 @@ _cpp_scan_out_logical_line (cpp_reader * fmacro.node = NULL; fmacro.offset = 0; fmacro.line = 0; + fmacro.paramc = 0; fmacro.argc = 0; quote = 0; header_ok = pfile->state.angled_headers; CUR (pfile->context) = pfile->buffer->cur; RLIMIT (pfile->context) = pfile->buffer->rlimit; - pfile->out.cur = pfile->out.base; - pfile->out.first_line = pfile->line_table->highest_line; + if (!builtin_macro_arg) + { + pfile->out.cur = pfile->out.base; + pfile->out.first_line = pfile->line_table->highest_line; + } /* start_of_input_line is needed to make sure that directives really, really start at the first character of the line. */ start_of_input_line = pfile->buffer->cur; @@ -379,6 +407,7 @@ _cpp_scan_out_logical_line (cpp_reader * for (;;) { if (!context->prev + && !builtin_macro_arg && cur >= pfile->buffer->notes[pfile->buffer->cur_note].pos) { pfile->buffer->cur = cur; @@ -410,6 +439,8 @@ _cpp_scan_out_logical_line (cpp_reader * /* Omit the newline from the output buffer. */ pfile->out.cur = out - 1; pfile->buffer->cur = cur; + if (builtin_macro_arg) + goto done; pfile->buffer->need_line = true; CPP_INCREMENT_LINE (pfile, 0); @@ -489,8 +520,7 @@ _cpp_scan_out_logical_line (cpp_reader * { /* Macros invalidate MI optimization. */ pfile->mi_valid = false; - if (! (node->flags & NODE_BUILTIN) - && node->value.macro->fun_like) + if (fun_like_macro (node)) { maybe_start_funlike (pfile, node, out_start, &fmacro); lex_state = ls_fun_open; @@ -572,6 +602,91 @@ _cpp_scan_out_logical_line (cpp_reader * paren_depth--; if (lex_state == ls_fun_close && paren_depth == 0) { + if (fmacro.node->flags & NODE_BUILTIN) + { + /* Handle builtin function-like macros like + __has_attribute. The already parsed arguments + are put into a buffer, which is then preprocessed + and the result is fed to _cpp_push_text_context + with disabled expansion, where the ISO preprocessor + parses it. */ + lex_state = ls_none; + save_argument (&fmacro, out - pfile->out.base); + cpp_macro m; + memset (&m, '\0', sizeof (m)); + m.paramc = fmacro.paramc; + if (_cpp_arguments_ok (pfile, &m, fmacro.node, + fmacro.argc)) + { + size_t len = fmacro.args[1] - fmacro.args[0]; + uchar *buf; + + /* Remove the macro's invocation from the + output, and push its replacement text. */ + pfile->out.cur = pfile->out.base + fmacro.offset; + CUR (context) = cur; + buf = _cpp_unaligned_alloc (pfile, len + 2); + buf[0] = '('; + memcpy (buf + 1, pfile->out.base + fmacro.args[0], + len); + buf[len + 1] = '\n'; + + const unsigned char *ctx_rlimit = RLIMIT (context); + const unsigned char *saved_cur = pfile->buffer->cur; + const unsigned char *saved_rlimit + = pfile->buffer->rlimit; + const unsigned char *saved_line_base + = pfile->buffer->line_base; + bool saved_need_line = pfile->buffer->need_line; + cpp_buffer *saved_overlaid_buffer + = pfile->overlaid_buffer; + pfile->buffer->cur = buf; + pfile->buffer->line_base = buf; + pfile->buffer->rlimit = buf + len + 1; + pfile->buffer->need_line = false; + pfile->overlaid_buffer = pfile->buffer; + bool saved_in_directive = pfile->state.in_directive; + pfile->state.in_directive = true; + cpp_context *saved_prev_context = context->prev; + context->prev = NULL; + + _cpp_scan_out_logical_line (pfile, NULL, true); + + pfile->state.in_directive = saved_in_directive; + check_output_buffer (pfile, 1); + *pfile->out.cur = '\n'; + pfile->buffer->cur = pfile->out.base + fmacro.offset; + pfile->buffer->line_base = pfile->buffer->cur; + pfile->buffer->rlimit = pfile->out.cur; + CUR (context) = pfile->buffer->cur; + RLIMIT (context) = pfile->buffer->rlimit; + + pfile->state.prevent_expansion++; + const uchar *text + = _cpp_builtin_macro_text (pfile, fmacro.node); + pfile->state.prevent_expansion--; + + context->prev = saved_prev_context; + pfile->buffer->cur = saved_cur; + pfile->buffer->rlimit = saved_rlimit; + pfile->buffer->line_base = saved_line_base; + pfile->buffer->need_line = saved_need_line; + pfile->overlaid_buffer = saved_overlaid_buffer; + pfile->out.cur = pfile->out.base + fmacro.offset; + CUR (context) = cur; + RLIMIT (context) = ctx_rlimit; + len = ustrlen (text); + buf = _cpp_unaligned_alloc (pfile, len + 1); + memcpy (buf, text, len); + buf[len] = '\n'; + text = buf; + _cpp_push_text_context (pfile, fmacro.node, + text, len); + goto new_context; + } + break; + } + cpp_macro *m = fmacro.node->value.macro; m->used = 1; @@ -588,8 +703,7 @@ _cpp_scan_out_logical_line (cpp_reader * { /* Remove the macro's invocation from the output, and push its replacement text. */ - pfile->out.cur = (pfile->out.base - + fmacro.offset); + pfile->out.cur = pfile->out.base + fmacro.offset; CUR (context) = cur; replace_args_and_push (pfile, &fmacro); goto new_context; @@ -711,7 +825,7 @@ push_replacement_text (cpp_reader *pfile len = ustrlen (text); buf = _cpp_unaligned_alloc (pfile, len + 1); memcpy (buf, text, len); - buf[len]='\n'; + buf[len] = '\n'; text = buf; } else @@ -742,7 +856,7 @@ recursive_macro (cpp_reader *pfile, cpp_ detect true recursion; instead we assume any expansion more than 20 deep since the first invocation of this macro must be recursing. */ - if (recursing && node->value.macro->fun_like) + if (recursing && fun_like_macro (node)) { size_t depth = 0; cpp_context *context = pfile->context; @@ -1080,7 +1194,7 @@ _cpp_create_trad_definition (cpp_reader CPP_OPTION (pfile, discard_comments_in_macro_exp)); pfile->state.prevent_expansion++; - _cpp_scan_out_logical_line (pfile, macro); + _cpp_scan_out_logical_line (pfile, macro, false); pfile->state.prevent_expansion--; if (!macro) --- gcc/testsuite/c-c++-common/cpp/pr65238-1.c.jj 2015-03-11 17:12:26.909996912 +0100 +++ gcc/testsuite/c-c++-common/cpp/pr65238-1.c 2015-03-11 17:12:26.909996912 +0100 @@ -0,0 +1,53 @@ +/* PR preprocessor/65238 */ +/* { dg-do run } */ + +#define A unused +#define B A +#define C B +#define D __has_attribute(unused) +#define E __has_attribute(C) +#define F(X) __has_attribute(X) +#if !__has_attribute(unused) +#error unused attribute not supported - 1 +#endif +#if !__has_attribute(C) +#error unused attribute not supported - 2 +#endif +#if !D +#error unused attribute not supported - 3 +#endif +#if !E +#error unused attribute not supported - 4 +#endif +#if !F(unused) +#error unused attribute not supported - 5 +#endif +#if !F(C) +#error unused attribute not supported - 6 +#endif +int a = __has_attribute (unused) + __has_attribute (C) + D + E + F (unused) + F (C); +int b = __has_attribute (unused); +int c = __has_attribute (C); +int d = D; +int e = E; +int f = F (unused); +int g = F (C); +int h = __has_attribute ( + unused +) + __has_attribute ( + +C) + F ( +unused + +) + F +( +C +); + +int +main () +{ + if (a != 6 || b != 1 || c != 1 || d != 1 || e != 1 || f != 1 || g != 1 || h != 4) + __builtin_abort (); + return 0; +} --- gcc/testsuite/gcc.dg/cpp/pr65238-2.c.jj 2015-03-11 17:12:26.909996912 +0100 +++ gcc/testsuite/gcc.dg/cpp/pr65238-2.c 2015-03-11 20:00:23.235269177 +0100 @@ -0,0 +1,18 @@ +/* PR preprocessor/65238 */ +/* { dg-do preprocess } */ + +#if __has_attribute( +#endif +#if __has_attribute(unused +#endif +#if __has_attribute(unused, unused) +#endif +#if __has_attribute(__has_attribute(unused)) +#endif + +/* { dg-error "macro .__has_attribute. requires an identifier" "" {target "*-*-*"} 4 } */ +/* { dg-error "missing ... after .__has_attribute." "" {target "*-*-*"} 6 } */ +/* { dg-error "missing ... after .__has_attribute." "" {target "*-*-*"} 8 } */ +/* { dg-error "missing binary operator before token .unused." "" {target "*-*-*"} 8 } */ +/* { dg-error "macro .__has_attribute. requires an identifier" "" {target "*-*-*"} 10 } */ +/* { dg-error "missing ... in expression" "" {target "*-*-*"} 10 } */ --- gcc/testsuite/gcc.dg/cpp/trad/pr65238-3.c.jj 2015-03-11 17:12:26.909996912 +0100 +++ gcc/testsuite/gcc.dg/cpp/trad/pr65238-3.c 2015-03-11 17:12:26.909996912 +0100 @@ -0,0 +1,5 @@ +/* PR preprocessor/65238 */ +/* { dg-do run } */ +/* { dg-options "-traditional-cpp" } */ + +#include "../../../c-c++-common/cpp/pr65238-1.c" --- gcc/testsuite/gcc.dg/cpp/trad/pr65238-4.c.jj 2015-03-11 17:12:26.909996912 +0100 +++ gcc/testsuite/gcc.dg/cpp/trad/pr65238-4.c 2015-03-11 17:12:26.909996912 +0100 @@ -0,0 +1,19 @@ +/* PR preprocessor/65238 */ +/* { dg-do preprocess } */ +/* { dg-options "-traditional-cpp" } */ + +#if __has_attribute( +#endif +#if __has_attribute(unused +#endif +#if __has_attribute(unused, unused) +#endif +#if __has_attribute(__has_attribute(unused)) +#endif + +/* { dg-error "unterminated argument list invoking macro .__has_attribute." "" {target "*-*-*"} 5 } */ +/* { dg-error "#if with no expression" "" {target "*-*-*"} 5 } */ +/* { dg-error "unterminated argument list invoking macro .__has_attribute." "" {target "*-*-*"} 7 } */ +/* { dg-error "macro .__has_attribute. passed 2 arguments, but takes just 1" "" {target "*-*-*"} 9 } */ +/* { dg-error "missing ... in expression" "" {target "*-*-*"} 9 } */ +/* { dg-error "macro .__has_attribute. requires an identifier" "" {target "*-*-*"} 11 } */ Jakub