@@ -280,7 +280,7 @@ token_streamer::stream (cpp_reader *pfile, const cpp_token *token,
const char *space;
const char *name;
- line_marker_emitted = maybe_print_line (token->src_loc);
+ line_marker_emitted = maybe_print_line (loc);
fputs ("#pragma ", print.outf);
c_pp_lookup_pragma (token->val.pragma, &space, &name);
if (space)
@@ -1,4 +1,5 @@
// { dg-do compile }
+// { dg-additional-options "-ftrack-macro-expansion=0" }
#pragma GCC warning "warn-a" // { dg-warning warn-a }
#pragma GCC error "err-b" // { dg-error err-b }
@@ -1,6 +1,6 @@
/* PR preprocessor/57580 */
/* { dg-do compile } */
-/* { dg-options "-save-temps" } */
+/* { dg-options "-save-temps -ftrack-macro-expansion=0" } */
#define MSG \
_Pragma("message(\"message0\")") \
@@ -8,7 +8,8 @@ void
f (void)
{
const char *str = outer(inner(1,2)); /* { dg-line str_location } */
- /* { dg-warning "35:'pragma omp error' encountered: Test" "" { target *-*-* } inner_location }
+ /* { dg-warning "1:'pragma omp error' encountered: Test" "" { target *-*-* } 1 }
+ { dg-note "35: in <_Pragma directive>" "" { target *-*-* } inner_location }
{ dg-note "20:in expansion of macro 'inner'" "" { target *-*-* } outer_location }
{ dg-note "21:in expansion of macro 'outer'" "" { target *-*-* } str_location } */
}
@@ -8,7 +8,8 @@ void
f (void)
{
const char *str = outer(inner(1,2)); /* { dg-line str_location } */
- /* { dg-warning "35:'pragma omp error' encountered: Test" "" { target *-*-* } inner_location }
+ /* { dg-warning "4:'pragma omp error' encountered: Test" "" { target *-*-* } 1 }
+ { dg-note "35:in <_Pragma directive>" "" { target *-*-*} inner_location }
{ dg-note "20:in expansion of macro 'inner'" "" { target *-*-* } outer_location }
{ dg-note "21:in expansion of macro 'outer'" "" { target *-*-* } str_location } */
}
new file mode 100644
@@ -0,0 +1,35 @@
+/* Test virtual location aspects of _Pragmas, when an error is reported after
+ lexing the tokens from the _Pragma string. */
+/* { dg-additional-options "-Wpragmas -Wunknown-pragmas" } */
+
+_Pragma("GCC diagnostic ignored \"oops1\"") /* { dg-note {1:in <_Pragma directive>} } */
+/* { dg-warning {24:'oops1' is not an option} "" { target *-*-* } 1 } */
+
+#define S2 "GCC diagnostic ignored \"oops2\""
+_Pragma(S2) /* { dg-note {1:in <_Pragma directive>} } */
+/* { dg-warning {24:'oops2' is not an option} "" { target *-*-* } 1 } */
+
+#define PP(x) _Pragma(x) /* { dg-note {15:in <_Pragma directive>} } */
+PP("GCC diagnostic ignored \"oops3\"") /* { dg-note {1:in expansion of macro 'PP'} } */
+/* { dg-warning {24:'oops3' is not an option} "" { target *-*-* } 1 } */
+
+#define X4 _Pragma("GCC diagnostic ignored \"oops4\"") /* { dg-note {12:in <_Pragma directive>} } */
+#define Y4 X4 /* { dg-note {12:in expansion of macro 'X4'} } */
+Y4 /* { dg-note {1:in expansion of macro 'Y4'} } */
+/* { dg-warning {24:'oops4' is not an option} "" { target *-*-* } 1 } */
+
+#define P5 _Pragma /* { dg-note {12:in <_Pragma directive>} } */
+#define S5 "GCC diagnostic ignored \"oops5\""
+#define Y5 P5(S5) /* { dg-note {12:in expansion of macro 'P5'} } */
+Y5 /* { dg-note {1:in expansion of macro 'Y5'} } */
+/* { dg-warning {24:'oops5' is not an option} "" { target *-*-* } 1 } */
+
+#define P6 _Pragma /* { dg-note {12:in <_Pragma directive>} } */
+#define X6 P6("GCC diagnostic ignored \"oops6\"") /* { dg-note {12:in expansion of macro 'P6'} } */
+X6 /* { dg-note {1:in expansion of macro 'X6'} } */
+/* { dg-warning {24:'oops6' is not an option} "" { target *-*-* } 1 } */
+
+_Pragma(__DATE__) /* { dg-warning {-:[-Wunknown-pragmas]} } */
+
+_Pragma("once") /* { dg-note {1:in <_Pragma directive>} } */
+/* { dg-warning {#pragma once in main file} "" { target *-*-*} 1 } */
new file mode 100644
@@ -0,0 +1,18 @@
+/* Test virtual location aspects of _Pragmas, when an error is reported during
+ lexing of the _Pragma string itself or of the tokens within it. */
+/* { dg-additional-options "-Wpragmas" } */
+
+#define X1 "\""
+_Pragma(X1) /* { dg-note {1:in <_Pragma directive>} } */
+/* { dg-warning {1:missing terminating " character} "" { target *-*-* } 1 } */
+
+#define X2a _Pragma("GCC warning \"hello\"") ( /* { dg-note {13:in <_Pragma directive>} } */
+#define X2b "GCC warning \"goodbye\"" )
+_Pragma X2a X2b /* { dg-note {9:in expansion of macro 'X2a'} } */
+/* { dg-note {1:in <_Pragma directive>} "" { target *-*-* } .-1 } */
+/* { dg-warning {13:hello} "" { target *-*-* } 1 } */
+/* { dg-warning {13:goodbye} "" { target *-*-* } 1 } */
+
+_Pragma() /* { dg-error {9:_Pragma takes a parenthesized string literal} } */
+/* { dg-note {1:in <_Pragma directive>} "" { target *-*-* } .-1 } */
+/* { dg-error {at end of input|'_Pragma' does not name a type} "" { target *-*-* } .-2 } */
new file mode 100644
@@ -0,0 +1,16 @@
+/* Test that _Pragma with a raw string works correctly. */
+/* { dg-do compile { target c++11 } } */
+/* { dg-additional-options "-Wunused-variable -Wpragmas" } */
+
+_Pragma(R"delim(GCC diagnostic push)delim")
+_Pragma(R"(GCC diagnostic ignored "-Wunused-variable")")
+void f1 () { int i; }
+_Pragma(R"(GCC diagnostic pop)")
+void f2 () { int i; } /* { dg-warning {18:-Wunused-variable} } */
+
+/* Make sure lines stay in sync if there is an embedded newline too. */
+_Pragma(R"xyz(GCC diagnostic ignored R"(two
+line option?)")xyz")
+/* { dg-note {1:in <_Pragma directive>} "" { target *-*-* } .-2 } */
+/* { dg-warning {24:unknown option} "" { target *-*-* } 1 } */
+void f3 () { int i; } /* { dg-warning {18:-Wunused-variable} } */
new file mode 100644
@@ -0,0 +1,20 @@
+#include "LC_GEN-maps.H"
+
+/* The LC_GEN map was written to the PCH, but there is not currently a way to
+ observe that fact in normal user code. Let's try to test it anyway, using
+ -fdump-internal-locations to inspect the line_maps object we received from
+ the PCH. */
+
+/* { dg-additional-options -fdump-internal-locations } */
+/* { dg-allow-blank-lines-in-output "" } */
+
+/* These regexps themselves will also appear in the output of
+ -fdump-internal-locations, so we need to make sure they contain at least
+ some regexp special characters, even if not strictly necessary, so they
+ match the intended text only, and not themselves. Also, we make the second
+ one intentionally match the whole output if it maches anything. We could
+ use dg-excess-errors instead, but that outputs XFAILS which are not really
+ helpful for this test. */
+
+/* { dg-regexp {reason: . \(LC_GEN\)} } */
+/* { dg-regexp {(.|[\n\r])*file: this string should end up in the "PCH"(.|[\n\r])*} } */
new file mode 100644
@@ -0,0 +1,5 @@
+/* Evaluating the _Pragma directive here creates an LC_GEN map in the
+ line_maps object that will be stored in the PCH. The test will make sure
+ that the buffer holding the de-stringified _Pragma string contents makes
+ its way there. */
+_Pragma("this string should end up in the \"PCH\"")
@@ -1,2 +1,3 @@
+/* { dg-additional-options "-ftrack-macro-expansion=0" } */
#include "operator-1.H"
int main(void){ major(0);} /* { dg-warning "Did not Work" } */
@@ -2,5 +2,6 @@
/* PR preprocessor/28165 */
/* { dg-do preprocess } */
+/* { dg-additional-options "-ftrack-macro-expansion=0" } */
#pragma GCC system_header /* { dg-warning "system_header" "ignored" } */
_Pragma ("GCC system_header") /* { dg-warning "system_header" "ignored" } */
@@ -1,4 +1,5 @@
/* Test case for PR 35322 -- _Pragma ICE. */
/* { dg-do preprocess } */
+/* { dg-additional-options "-ftrack-macro-expansion=0" } */
_Pragma("GCC dependency") /* { dg-error "#pragma dependency expects" } */
@@ -1,4 +1,5 @@
/* { dg-do compile } */
+/* { dg-additional-options -ftrack-macro-expansion=0 } */
/* N1312 7.1.1: The FLOAT_CONST_DECIMAL64 pragma.
C99 6.4.4.2a (New).
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-std=c99 -pedantic" } */
+/* { dg-options "-std=c99 -pedantic -ftrack-macro-expansion=0" } */
/* N1312 7.1.1: The FLOAT_CONST_DECIMAL64 pragma.
C99 6.4.4.2a (New).
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-std=c99 -pedantic-errors" } */
+/* { dg-options "-std=c99 -pedantic-errors -ftrack-macro-expansion=0" } */
/* N1312 7.1.1: The FLOAT_CONST_DECIMAL64 pragma.
C99 6.4.4.2a (New).
@@ -1,6 +1,6 @@
/* PR preprocessor/27746 */
/* { dg-do compile } */
-/* { dg-options "-fopenmp -Wunknown-pragmas" } */
+/* { dg-options "-fopenmp -Wunknown-pragmas -ftrack-macro-expansion=0" } */
#define p _Pragma ("omp parallel")
#define omp_p _Pragma ("omp p")
@@ -45,8 +45,9 @@
#define DO_PRAGMA(x) _Pragma (#x) /* { dg-line pragma_loc1 } */
#define TODO(x) DO_PRAGMA(message ("TODO - " #x)) /* { dg-line pragma_loc2 } */
TODO(Okay 4) /* { dg-message "in expansion of macro 'TODO'" } */
-/* { dg-message "TODO - Okay 4" "test4.1" { target *-*-* } pragma_loc1 } */
+/* { dg-message "1:TODO - Okay 4" "test4.1" { target *-*-* } 1 } */
/* { dg-message "in expansion of macro 'DO_PRAGMA'" "test4.2" { target *-*-* } pragma_loc2 } */
+/* { dg-note {in <_Pragma directive>} "test4.3" { target *-*-* } pragma_loc1 } */
#if 0
#pragma message ("Not printed")
@@ -54,6 +54,7 @@ proc prune_gcc_output { text } {
# Diagnostic inclusion stack
regsub -all "(^|\n)(In file)?\[ \]+included from \[^\n\]*" $text "" text
+ regsub -all "(^|\n)In buffer generated from \[^\n\]*" $text "" text
regsub -all "(^|\n)\[ \]+from \[^\n\]*" $text "" text
regsub -all "(^|\n)(In|of) module( \[^\n \]*,)? imported at \[^\n\]*" $text "" text
@@ -203,9 +203,12 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
const int resolved_def_loc_line = SOURCE_LINE (m, l0);
if (ix == 0 && saved_location_line != resolved_def_loc_line)
{
- diagnostic_append_note (context, resolved_def_loc,
- "in definition of macro %qs",
- linemap_map_get_macro_name (iter->map));
+ const char *name = linemap_map_get_macro_name (iter->map);
+ if (*name == '<')
+ diagnostic_append_note (context, resolved_def_loc, "in %s", name);
+ else
+ diagnostic_append_note (context, resolved_def_loc,
+ "in definition of macro %qs", name);
/* At this step, as we've printed the context of the macro
definition, we don't want to print the context of its
expansion, otherwise, it'd be redundant. */
@@ -220,9 +223,12 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
MACRO_MAP_EXPANSION_POINT_LOCATION (iter->map),
LRK_MACRO_DEFINITION_LOCATION, NULL);
- diagnostic_append_note (context, resolved_exp_loc,
- "in expansion of macro %qs",
- linemap_map_get_macro_name (iter->map));
+ const char *name = linemap_map_get_macro_name (iter->map);
+ if (*name == '<')
+ diagnostic_append_note (context, resolved_exp_loc, "in %s", name);
+ else
+ diagnostic_append_note (context, resolved_exp_loc,
+ "in expansion of macro %qs", name);
}
}
@@ -127,10 +127,10 @@ static void do_pragma_warning_or_error (cpp_reader *, bool error);
static void do_pragma_warning (cpp_reader *);
static void do_pragma_error (cpp_reader *);
static void do_linemarker (cpp_reader *);
-static const cpp_token *get_token_no_padding (cpp_reader *);
-static const cpp_token *get__Pragma_string (cpp_reader *);
-static void destringize_and_run (cpp_reader *, const cpp_string *,
- location_t);
+static const cpp_token *get_token_no_padding (cpp_reader *,
+ location_t * = nullptr);
+static const cpp_token *get__Pragma_string (cpp_reader *,
+ location_t * = nullptr);
static bool parse_answer (cpp_reader *, int, location_t, cpp_macro **);
static cpp_hashnode *parse_assertion (cpp_reader *, int, cpp_macro **);
static cpp_macro **find_answer (cpp_hashnode *, const cpp_macro *);
@@ -1501,14 +1501,12 @@ do_pragma (cpp_reader *pfile)
{
const struct pragma_entry *p = NULL;
const cpp_token *token, *pragma_token;
- location_t pragma_token_virt_loc = 0;
cpp_token ns_token;
unsigned int count = 1;
pfile->state.prevent_expansion++;
- pragma_token = token = cpp_get_token_with_location (pfile,
- &pragma_token_virt_loc);
+ pragma_token = token = cpp_get_token (pfile);
ns_token = *token;
if (token->type == CPP_NAME)
{
@@ -1534,7 +1532,7 @@ do_pragma (cpp_reader *pfile)
{
if (p->is_deferred)
{
- pfile->directive_result.src_loc = pragma_token_virt_loc;
+ pfile->directive_result.src_loc = pragma_token->src_loc;
pfile->directive_result.type = CPP_PRAGMA;
pfile->directive_result.flags = pragma_token->flags;
pfile->directive_result.val.pragma = p->u.ident;
@@ -1827,11 +1825,11 @@ do_pragma_error (cpp_reader *pfile)
/* Get a token but skip padding. */
static const cpp_token *
-get_token_no_padding (cpp_reader *pfile)
+get_token_no_padding (cpp_reader *pfile, location_t *virt_loc)
{
for (;;)
{
- const cpp_token *result = cpp_get_token (pfile);
+ const cpp_token *result = cpp_get_token_with_location (pfile, virt_loc);
if (result->type != CPP_PADDING)
return result;
}
@@ -1840,7 +1838,7 @@ get_token_no_padding (cpp_reader *pfile)
/* Check syntax is "(string-literal)". Returns the string on success,
or NULL on failure. */
static const cpp_token *
-get__Pragma_string (cpp_reader *pfile)
+get__Pragma_string (cpp_reader *pfile, location_t *string_virt_loc)
{
const cpp_token *string;
const cpp_token *paren;
@@ -1851,7 +1849,7 @@ get__Pragma_string (cpp_reader *pfile)
if (paren->type != CPP_OPEN_PAREN)
return NULL;
- string = get_token_no_padding (pfile);
+ string = get_token_no_padding (pfile, string_virt_loc);
if (string->type == CPP_EOF)
_cpp_backup_tokens (pfile, 1);
if (string->type != CPP_STRING && string->type != CPP_WSTRING
@@ -1871,55 +1869,105 @@ get__Pragma_string (cpp_reader *pfile)
/* Destringize IN into a temporary buffer, by removing the first \ of
\" and \\ sequences, and process the result as a #pragma directive. */
static void
-destringize_and_run (cpp_reader *pfile, const cpp_string *in,
- location_t expansion_loc)
-{
- const unsigned char *src, *limit;
- char *dest, *result;
- cpp_context *saved_context;
- cpp_token *saved_cur_token;
- tokenrun *saved_cur_run;
- cpp_token *toks;
- int count;
- const struct directive *save_directive;
-
- dest = result = (char *) alloca (in->len - 1);
- src = in->text + 1 + (in->text[0] == 'L');
- limit = in->text + in->len - 1;
- while (src < limit)
+destringize_and_run (cpp_reader *pfile, _cpp__Pragma_state *pstate)
+{
+ uchar *dest, *result;
+
+ /* Determine where the data starts, and what kind of string it is. */
+ const cpp_string *const in = &pstate->string_tok->val.str;
+ const uchar *src = in->text;
+ bool is_raw_string = false;
+ for (;;)
{
- /* We know there is a character following the backslash. */
- if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
- src++;
- *dest++ = *src++;
+ switch (*src++)
+ {
+ case '\"': break;
+ case 'R': is_raw_string = true; continue;
+ case '\0': gcc_assert (false);
+ default: continue;
+ }
+ break;
}
- *dest = '\n';
- /* Ugh; an awful kludge. We are really not set up to be lexing
- tokens when in the middle of a macro expansion. Use a new
- context to force cpp_get_token to lex, and so skip_rest_of_line
- doesn't go beyond the end of the text. Also, remember the
- current lexing position so we can return to it later.
+ /* If we were given a raw string literal, we don't need to destringize it,
+ but we do need to strip off the prefix and the suffix. */
+ if (is_raw_string)
+ {
+ cpp_string buf;
+ const bool ok
+ = cpp_interpret_string_notranslate (pfile, in, 1, &buf, CPP_STRING);
+ gcc_assert (ok);
- Something like line-at-a-time lexing should remove the need for
- this. */
- saved_context = pfile->context;
- saved_cur_token = pfile->cur_token;
- saved_cur_run = pfile->cur_run;
+ /* BUF.TEXT ends with a terminating null (which is counted in BUF.LEN).
+ We want to end with a newline as required by cpp_push_buffer. While it
+ is not strictly necessary to null terminate our buffer, it is useful to
+ do so for safety, so we reserve one extra byte. The \n\0 sequence is
+ appended after the else block. */
+ result = _cpp_unaligned_alloc (pfile, buf.len + 1);
+ memcpy (result, buf.text, buf.len - 1);
+ dest = result + (buf.len - 1);
+ XDELETEVEC (buf.text);
+ }
+ else
+ {
+ const auto last_ptr = in->text + in->len - 1;
+ /* +2 for the trailing \n\0 as above. */
+ dest = result = _cpp_unaligned_alloc (pfile, last_ptr - src + 1 + 2);
+ while (src < last_ptr)
+ {
+ /* We know there is a character following the backslash. */
+ if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
+ src++;
+ *dest++ = *src++;
+ }
+ }
+ *dest++ = '\n';
+ *dest++ = '\0';
- pfile->context = XCNEW (cpp_context);
+ /* We will now ask PFILE to interrupt what it was doing (obtaining tokens
+ either from the main context via lexing, or from a macro context), and get
+ tokens from the string argument instead. We create a new isolated
+ cpp_context so that cpp_get_token will think it is working on the main
+ buffer and call cpp_lex_token accordingly. Save all the relevant state so
+ we can return to the previous task once that is completed.
- /* Inline run_directive, since we need to delay the _cpp_pop_buffer
- until we've read all of the tokens that we want. */
- cpp_push_buffer (pfile, (const uchar *) result, dest - result,
- /* from_stage3 */ true);
- /* ??? Antique Disgusting Hack. What does this do? */
- if (pfile->buffer->prev)
- pfile->buffer->file = pfile->buffer->prev->file;
+ Doing things this way is a bit of a kludge, but the alternative would be
+ to create a new context type to support lexing from a string, and that
+ would add overhead to every token parse, while _Pragma is relatively rarely
+ needed. */
+ const auto saved_context = pfile->context;
+ const auto saved_cur_token = pfile->cur_token;
+ const auto saved_cur_run = pfile->cur_run;
+ pfile->context = XCNEW (cpp_context);
start_directive (pfile);
+
+ /* Set up an LC_GEN line map to get valid locations for the tokens we are
+ about to lex. We need to do this after calling start_directive, because
+ historically pfile->directive_line is what's been passed to
+ pfile->cb.def_pragma, and we are not proposing to change that now. To
+ decide if we are in a system header or not, look at the location of the
+ _Pragma token. So for instance if we have _Pragma(S) in the main file,
+ where S is a macro defined in a system header, we will decide we are not in
+ a system location. */
+ const unsigned int buf_len = dest - result;
+ const int sysp = linemap_location_in_system_header_p (pfile->line_table,
+ pstate->pragma_loc);
+ linemap_add (pfile->line_table, LC_GEN, sysp, (const char *)result, 1,
+ buf_len);
+ const auto col_hint = (uchar *) memchr (result, '\n', buf_len) - result;
+ linemap_line_start (pfile->line_table, 1, col_hint);
+
+ /* Push the buffer. */
+ cpp_push_buffer (pfile, result, buf_len - 2, true);
+
+ /* This is needed to make _Pragma("once") work correctly, as it needs
+ pfile->buffer->file to be set to the current source file. */
+ pfile->buffer->file = pfile->buffer->prev->file;
+
+ /* We are ready to start handling the directive as normal. */
_cpp_clean_line (pfile);
- save_directive = pfile->directive;
+ const auto save_directive = pfile->directive;
pfile->directive = &dtable[T_PRAGMA];
do_pragma (pfile);
if (pfile->directive_result.type == CPP_PRAGMA)
@@ -1928,80 +1976,123 @@ destringize_and_run (cpp_reader *pfile, const cpp_string *in,
pfile->directive = save_directive;
/* We always insert at least one token, the directive result. It'll
- either be a CPP_PADDING or a CPP_PRAGMA. In the later case, we
+ either be a CPP_PADDING or a CPP_PRAGMA. In the latter case, we
need to insert *all* of the tokens, including the CPP_PRAGMA_EOL. */
/* If we're not handling the pragma internally, read all of the tokens from
- the string buffer now, while the string buffer is still installed. */
- /* ??? Note that the token buffer allocated here is leaked. It's not clear
- to me what the true lifespan of the tokens are. It would appear that
- the lifespan is the entire parse of the main input stream, in which case
- this may not be wrong. */
- if (pfile->directive_result.type == CPP_PRAGMA)
- {
- int maxcount;
-
- count = 1;
- maxcount = 50;
- toks = XNEWVEC (cpp_token, maxcount);
- toks[0] = pfile->directive_result;
- toks[0].src_loc = expansion_loc;
-
- do
+ the string buffer now, while the string buffer is still installed, and then
+ push them as a new token context after. This way, we can clean up the
+ temporarily modified state of the lexer now. */
+
+ const bool is_deferred = (pfile->directive_result.type == CPP_PRAGMA);
+ if (is_deferred)
+ {
+ /* Using _cpp_buff allows us to arrange for this buffer to be freed when
+ the new token context is popped, without adding any additional space
+ overhead to the cpp_context structure. In order to support
+ track_macro_expansion==0, we need to store the cpp_token objects
+ contiguously, and the virt locs separately. (Note that these tokens
+ may acquire a virtual loc here, in case the pragma allows macro
+ expansion. But they will not yet have virtual locs representing them
+ as part of the expansion of the _Pragma directive; this will be handled
+ later in _cpp_push__Pragma_token_context. */
+ const size_t init_count = 50;
+ _cpp_buff *tok_buff
+ = _cpp_get_buff (pfile, init_count * sizeof (cpp_token));
+ _cpp_buff *loc_buff
+ = _cpp_get_buff (pfile, init_count * sizeof (location_t));
+
+ /* Remember the base buffs so we can chain the final loc buff after it
+ once we are done collecting tokens. */
+ const auto tok_buff0 = tok_buff;
+ pstate->buff_chain = &loc_buff->next;
+
+ /* DIRECTIVE_RESULT is the first token we return (a CPP_PRAGMA). This
+ location cannot result from macro expansion, so there is no virtual
+ location to worry about. */
+ auto tok_out = (cpp_token *) tok_buff->base;
+ *tok_out++ = pfile->directive_result;
+ auto loc_out = (location_t *) loc_buff->base;
+ *loc_out++ = pfile->directive_result.src_loc;
+ unsigned int ntoks = 1;
+
+ /* Finally get all the tokens. */
+ for (;;)
{
- if (count == maxcount)
+ if (tok_buff->limit - (uchar *)tok_out < (int)sizeof (cpp_token))
{
- maxcount = maxcount * 3 / 2;
- toks = XRESIZEVEC (cpp_token, toks, maxcount);
+ _cpp_extend_buff (pfile, &tok_buff,
+ tok_buff->limit - tok_buff->base);
+ tok_out = ((cpp_token *)tok_buff->base) + ntoks;
}
- toks[count] = *cpp_get_token (pfile);
- /* _Pragma is a builtin, so we're not within a macro-map, and so
- the token locations are set to bogus ordinary locations
- near to, but after that of the "_Pragma".
- Paper over this by setting them equal to the location of the
- _Pragma itself (PR preprocessor/69126). */
- toks[count].src_loc = expansion_loc;
+
+ if (loc_buff->limit - (uchar *)loc_out < (int)sizeof (location_t))
+ {
+ _cpp_extend_buff (pfile, &loc_buff,
+ loc_buff->limit - loc_buff->base);
+ loc_out = ((location_t *)loc_buff->base) + ntoks;
+ }
+
+ const auto this_tok = tok_out;
+ *tok_out++ = *cpp_get_token_with_location (pfile, loc_out++);
+ ++ntoks;
+
/* Macros have been already expanded by cpp_get_token
if the pragma allowed expansion. */
- toks[count++].flags |= NO_EXPAND;
+ this_tok->flags |= NO_EXPAND;
+ if (this_tok->type == CPP_PRAGMA_EOL)
+ break;
}
- while (toks[count-1].type != CPP_PRAGMA_EOL);
+
+ /* Finalize the buffers so they can be stored as one chain in a
+ cpp_context and freed when that context is popped. */
+ tok_buff0->next = loc_buff;
+ pstate->ntoks = ntoks;
+ pstate->tok_buff = tok_buff;
+ pstate->loc_buff = loc_buff;
}
else
{
- count = 1;
- toks = &pfile->avoid_paste;
-
/* If we handled the entire pragma internally, make sure we get the
line number correct for the next token. */
if (pfile->cb.line_change)
pfile->cb.line_change (pfile, pfile->cur_token, false);
}
- /* Finish inlining run_directive. */
+ /* Reset the old state before... */
+ const auto map = linemap_add (pfile->line_table, LC_LEAVE, 0, nullptr, 0);
+ linemap_line_start
+ (pfile->line_table,
+ ORDINARY_MAP_STARTING_LINE_NUMBER (linemap_check_ordinary (map)),
+ 127);
pfile->buffer->file = NULL;
_cpp_pop_buffer (pfile);
-
- /* Reset the old macro state before ... */
XDELETE (pfile->context);
pfile->context = saved_context;
pfile->cur_token = saved_cur_token;
pfile->cur_run = saved_cur_run;
- /* ... inserting the new tokens we collected. */
- _cpp_push_token_context (pfile, NULL, toks, count);
+ /* ...inserting the new tokens we collected. This is not a simple call to
+ _cpp_push_token_context, because we need to create virtual locations
+ for the tokens and push an extended token context to return them. */
+ if (is_deferred)
+ _cpp_push__Pragma_token_context (pfile, pstate);
+ else
+ _cpp_push_token_context (pfile, nullptr, &pfile->avoid_paste, 1);
}
+
/* Handle the _Pragma operator. Return 0 on error, 1 if ok. */
+
int
-_cpp_do__Pragma (cpp_reader *pfile, location_t expansion_loc)
+_cpp_do__Pragma (cpp_reader *pfile, _cpp__Pragma_state *pstate)
{
- const cpp_token *string = get__Pragma_string (pfile);
- pfile->directive_result.type = CPP_PADDING;
+ pstate->string_tok = get__Pragma_string (pfile, &pstate->string_loc);
- if (string)
+ pfile->directive_result.type = CPP_PADDING;
+ if (pstate->string_tok)
{
- destringize_and_run (pfile, &string->val.str, expansion_loc);
+ destringize_and_run (pfile, pstate);
return 1;
}
cpp_error (pfile, CPP_DL_ERROR,
@@ -60,13 +60,11 @@ cpp_diagnostic_at (cpp_reader * pfile, enum cpp_diagnostic_level level,
enum cpp_warning_reason reason, rich_location *richloc,
const char *msgid, va_list *ap)
{
- bool ret;
-
if (!pfile->cb.diagnostic)
abort ();
- ret = pfile->cb.diagnostic (pfile, level, reason, richloc, _(msgid), ap);
-
- return ret;
+ if (pfile->diagnostic_rebase_loc)
+ _cpp_rebase_diagnostic_location (pfile, richloc);
+ return pfile->cb.diagnostic (pfile, level, reason, richloc, _(msgid), ap);
}
/* Print a diagnostic at the location of the previously lexed token. */
@@ -197,16 +195,14 @@ cpp_diagnostic_with_line (cpp_reader * pfile, enum cpp_diagnostic_level level,
location_t src_loc, unsigned int column,
const char *msgid, va_list *ap)
{
- bool ret;
-
if (!pfile->cb.diagnostic)
abort ();
rich_location richloc (pfile->line_table, src_loc);
if (column)
richloc.override_column (column);
- ret = pfile->cb.diagnostic (pfile, level, reason, &richloc, _(msgid), ap);
-
- return ret;
+ if (pfile->diagnostic_rebase_loc)
+ _cpp_rebase_diagnostic_location (pfile, &richloc);
+ return pfile->cb.diagnostic (pfile, level, reason, &richloc, _(msgid), ap);
}
/* Print a warning or error, depending on the value of LEVEL. */
@@ -1715,6 +1715,7 @@ class rich_location
location_range *get_range (unsigned int idx);
expanded_location get_expanded_location (unsigned int idx);
+ void forget_cached_expanded_location () { m_have_expanded_location = false; }
void
override_column (int column);
@@ -292,6 +292,28 @@ struct lexer_state
unsigned char ignore__Pragma;
};
+/* Because handling of _Pragma bounces back and forth between macro.cc and
+ directives.cc, it is useful to keep the needed state in one place. */
+struct _cpp__Pragma_state
+{
+ const cpp_token *string_tok; /* The token for the argument string. */
+
+ /* These locations are the virtual locations returned by
+ cpp_get_token_with_location, if the relevant tokens came from macro
+ expansions. */
+ location_t pragma_loc; /* Location of the _Pragma token. */
+ location_t string_loc; /* Location of the string arg. */
+
+ /* The tokens lexed from the _Pragma string. */
+ unsigned int ntoks;
+ _cpp_buff *tok_buff;
+ _cpp_buff *loc_buff;
+ _cpp_buff **buff_chain;
+};
+
+/* In macro.cc, implements pstate->diagnostic_rebase_loc handling. */
+void _cpp_rebase_diagnostic_location (cpp_reader *, rich_location *);
+
/* Special nodes - identifiers with predefined significance. */
struct spec_nodes
{
@@ -601,6 +623,12 @@ struct cpp_reader
zero of said file. */
location_t main_loc;
+ /* Location from which we would like to pretend a given token was
+ macro-expanded, if a diagnostic is issued. Useful for improving
+ _Pragma diagnostics. */
+ location_t diagnostic_rebase_loc;
+ cpp_hashnode *diagnostic_rebase_node;
+
/* Returns true iff we should warn about UTF-8 bidirectional control
characters. */
bool warn_bidi_p () const
@@ -701,6 +729,8 @@ extern const unsigned char *_cpp_builtin_macro_text (cpp_reader *,
extern int _cpp_warn_if_unused_macro (cpp_reader *, cpp_hashnode *, void *);
extern void _cpp_push_token_context (cpp_reader *, cpp_hashnode *,
const cpp_token *, unsigned int);
+extern void _cpp_push__Pragma_token_context (cpp_reader *,
+ _cpp__Pragma_state *);
extern void _cpp_backup_tokens_direct (cpp_reader *, unsigned int);
/* In identifiers.cc */
@@ -772,7 +802,7 @@ extern int _cpp_handle_directive (cpp_reader *, bool);
extern void _cpp_define_builtin (cpp_reader *, const char *);
extern char ** _cpp_save_pragma_names (cpp_reader *);
extern void _cpp_restore_pragma_names (cpp_reader *, char **);
-extern int _cpp_do__Pragma (cpp_reader *, location_t);
+extern int _cpp_do__Pragma (cpp_reader *, _cpp__Pragma_state *);
extern void _cpp_init_directives (cpp_reader *);
extern void _cpp_init_internal_pragmas (cpp_reader *);
extern void _cpp_do_file_change (cpp_reader *, enum lc_reason, const char *,
@@ -93,6 +93,8 @@ struct macro_arg_saved_data {
static const char *vaopt_paste_error =
N_("'##' cannot appear at either end of __VA_OPT__");
+static const uchar pragma_str[] = N_("<_Pragma directive>");
+
static void expand_arg (cpp_reader *, macro_arg *);
/* A class for tracking __VA_OPT__ state while iterating over a
@@ -756,7 +758,31 @@ builtin_macro (cpp_reader *pfile, cpp_hashnode *node,
if (pfile->state.in_directive || pfile->state.ignore__Pragma)
return 0;
- return _cpp_do__Pragma (pfile, loc);
+ _cpp__Pragma_state pstate = {};
+ pstate.pragma_loc = loc;
+
+ /* The diagnostic_rebase stuff arranges that any diagnostics issued during
+ lexing will point the user back to the _Pragma location. */
+ const auto prev_rloc = pfile->diagnostic_rebase_loc;
+ const auto prev_rnode = pfile->diagnostic_rebase_node;
+ pfile->diagnostic_rebase_loc = loc;
+ pfile->diagnostic_rebase_node
+ = cpp_lookup (pfile, pragma_str, (sizeof pragma_str) - 1);
+
+ /* While lexing tokens, if we end up expanding some macros, we would
+ like not to override top_most_macro_node; preserving it pointing
+ to the _Pragma helps out the case of -ftrack-macro-expansion=0.
+ Setting this flag causes in_macro_expansion_p to return TRUE,
+ even though we are not technically in a macro context. */
+ const bool prev_expand = pfile->about_to_expand_macro_p;
+ pfile->about_to_expand_macro_p = true;
+
+ /* Get the tokens, then reset everything back how it was. */
+ const int res = _cpp_do__Pragma (pfile, &pstate);
+ pfile->about_to_expand_macro_p = prev_expand;
+ pfile->diagnostic_rebase_loc = prev_rloc;
+ pfile->diagnostic_rebase_node = prev_rnode;
+ return res;
}
buf = _cpp_builtin_macro_text (pfile, node, expand_loc);
@@ -2802,7 +2828,8 @@ _cpp_pop_context (cpp_reader *pfile)
&& macro_of_context (context->prev) != macro)
macro->flags &= ~NODE_DISABLED;
- if (macro == pfile->top_most_macro_node && context->prev == NULL)
+ if (!pfile->about_to_expand_macro_p
+ && context->prev == &pfile->base_context)
/* We are popping the context of the top-most macro node. */
pfile->top_most_macro_node = NULL;
}
@@ -2836,10 +2863,10 @@ reached_end_of_context (cpp_context *context)
/* Consume the next token contained in the current context of PFILE,
and return it in *TOKEN. It's "full location" is returned in
- *LOCATION. If -ftrack-macro-location is in effeect, fFull location"
- means the location encoding the locus of the token across macro
- expansion; otherwise it's just is the "normal" location of the
- token which (*TOKEN)->src_loc. */
+ *LOCATION. If -ftrack-macro-location is in effect, "full location"
+ means the virtual location encoding the locus of the token across macro
+ expansion; otherwise it's just the "normal" (spelling) location of the
+ token, which is (*TOKEN)->src_loc. */
static inline void
consume_next_token_from_context (cpp_reader *pfile,
const cpp_token ** token,
@@ -4129,3 +4156,90 @@ cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node,
*buffer = '\0';
return pfile->macro_buffer;
}
+
+/* Handle the list of tokens lexed from a _Pragma string. We need to create
+ virtual locations (reflecting the fact that these tokens are logically
+ within the expansion of the _Pragma string), and push an extended token
+ context. */
+
+void
+_cpp_push__Pragma_token_context (cpp_reader *pfile,
+ _cpp__Pragma_state *pstate)
+{
+ const auto node = cpp_lookup (pfile, pragma_str, (sizeof pragma_str) - 1);
+ const auto toks = (const cpp_token *) pstate->tok_buff->base;
+
+ /* If not tracking macro expansions, then just push a normal token context.
+ cpp_get_token () will return the user the location of the _Pragma
+ directive, so they will have a valid location for the _Pragma which is
+ outside the LC_GEN map. */
+ if (!CPP_OPTION (pfile, track_macro_expansion))
+ {
+ _cpp_push_token_context (pfile, node, toks, pstate->ntoks);
+ /* Arrange to free the buffers when the context is popped. */
+ pfile->context->buff = pstate->tok_buff;
+ return;
+ }
+
+ location_t *virt_locs = nullptr;
+ _cpp_buff *const macro_tokens = tokens_buff_new (pfile, pstate->ntoks,
+ &virt_locs);
+ const auto map = linemap_enter_macro (pfile->line_table, node,
+ pstate->pragma_loc, pstate->ntoks);
+ const auto locs = (location_t *)pstate->loc_buff->base;
+ for (unsigned int i = 0; i != pstate->ntoks; ++i)
+ {
+ tokens_buff_add_token (macro_tokens, virt_locs, toks + i,
+ locs[i], locs[i], map, i);
+ }
+
+ /* Chain tok_buff ahead of macro_tokens so both are freed together
+ when the context is popped. pstate->buff_chain is the NEXT pointer
+ of the last buffer in the LOC_BUFF chain, so it looks like:
+ TOK_BUFF_1 -> ... -> TOK_BUFF_N -> ... -> LOC_BUFF_1 -> ... ->
+ LOC_BUFF_N -> MACRO_TOKENS_1 -> ... -> MACRO_TOKENS_N. */
+ *pstate->buff_chain = macro_tokens;
+ push_extended_tokens_context (pfile, node, pstate->tok_buff, virt_locs,
+ (const cpp_token **) macro_tokens->base,
+ pstate->ntoks);
+}
+
+void
+_cpp_rebase_diagnostic_location (cpp_reader *pfile, rich_location *richloc)
+{
+ /* If we are here, it means a diagnostic is being generated while lexing
+ tokens outside a macro context, but pfile->diagnostic_rebase_loc indicates
+ a location from which we would like to pretend we are actually expanding a
+ macro. This works around the fact that a macro map can only be generated
+ once we know how many tokens it will contain, but the number of tokens to
+ be lexed from, say, a _Pragma string, is not known ahead of time. In the
+ case of _Pragma, _cpp_push__Pragma_token_context above handles creating the
+ proper macro map once all the tokens are available. This function runs
+ earlier than that, while in the middle of lexing tokens, so it creates a
+ temporary macro map which serves only to improve the information content of
+ the diagnostic that's about to be generated. */
+
+ const int nlocs = richloc->get_num_locations ();
+
+ if (CPP_OPTION (pfile, track_macro_expansion))
+ {
+ const auto map
+ = linemap_enter_macro (pfile->line_table, pfile->diagnostic_rebase_node,
+ pfile->diagnostic_rebase_loc, nlocs);
+ for (int i = 0; i != nlocs; ++i)
+ {
+ location_range& r = *richloc->get_range (i);
+ r.m_loc = linemap_add_macro_token (map, i, r.m_loc, r.m_loc);
+ }
+ }
+ else
+ {
+ /* When not tracking macro expansion, then set the location to the
+ expansion point for all tokens, which is what would be returned
+ by cpp_get_token in the normal case. */
+ for (int i = 0; i != nlocs; ++i)
+ richloc->get_range (i)->m_loc = pfile->invocation_location;
+ }
+
+ richloc->forget_cached_expanded_location ();
+}
@@ -46,7 +46,8 @@ main (void)
/* Nvptx targets require a vector_length or 32 in to allow spinlocks with
gangs. */
check_reduction (num_workers (nw) vector_length (vl), worker); /* { dg-line check_reduction_loc } */
- /* { dg-warning "22:region is vector partitioned but does not contain vector partitioned code" "" { target *-*-* } pragma_loc }
+ /* { dg-warning "1:region is vector partitioned but does not contain vector partitioned code" "" { target *-*-* } 1 }
+ { dg-note "22:in <_Pragma directive>" "" { target *-*-* xfail offloading_enabled} pragma_loc }
{ dg-note "1:in expansion of macro 'DO_PRAGMA'" "" { target *-*-* xfail offloading_enabled } DO_PRAGMA_loc }
{ dg-note "3:in expansion of macro 'check_reduction'" "" { target *-*-* xfail offloading_enabled } check_reduction_loc }
TODO See PR101551 for 'offloading_enabled' XFAILs. */
@@ -40,46 +40,54 @@ int a1[n], a2[n];
gentest (test1, "acc parallel loop gang vector_length (128) firstprivate (t1, t2)",
"acc loop vector reduction(+:t1) reduction(-:t2)")
-/* { dg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
+/* { dg-warning {'t1' is used uninitialized} {} { target *-*-* } 1 }
+ { dg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
{ dg-note {'t1' was declared here} {} { target *-*-* } vars }
- { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
+ { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-5 }
TODO See PR101551 for 'offloading_enabled' differences. */
-/* { dg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+/* { dg-warning {'t2' is used uninitialized} {} { target *-*-* } 1 }
+ { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
{ dg-note {'t2' was declared here} {} { target *-*-* } vars }
- { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
+ { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-10 }
TODO See PR101551 for 'offloading_enabled' differences. */
gentest (test2, "acc parallel loop gang vector_length (128) firstprivate (t1, t2)",
"acc loop worker vector reduction(+:t1) reduction(-:t2)")
-/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } 1 }
+ { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
{ DUP_dg-note {'t1' was declared here} {} { target *-*-* } vars }
- { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
+ { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-5 }
TODO See PR101551 for 'offloading_enabled' differences. */
-/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } 1 }
+ { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
{ DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
- { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
+ { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-10 }
TODO See PR101551 for 'offloading_enabled' differences. */
gentest (test3, "acc parallel loop gang worker vector_length (128) firstprivate (t1, t2)",
"acc loop vector reduction(+:t1) reduction(-:t2)")
-/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } 1 }
+ { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
{ DUP_dg-note {'t1' was declared here} {} { target *-*-* } vars }
- { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
+ { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-5 }
TODO See PR101551 for 'offloading_enabled' differences. */
-/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } 1 }
+ { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
{ DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
- { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
+ { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-10 }
TODO See PR101551 for 'offloading_enabled' differences. */
gentest (test4, "acc parallel loop firstprivate (t1, t2)",
"acc loop reduction(+:t1) reduction(-:t2)")
-/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } 1 }
+ { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
{ DUP_dg-note {'t1' was declared here} {} { target *-*-* } vars }
- { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
+ { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-5 }
TODO See PR101551 for 'offloading_enabled' differences. */
-/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } 1 }
+ { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
{ DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
- { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
+ { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-10 }
TODO See PR101551 for 'offloading_enabled' differences. */