From patchwork Wed Aug 9 22:14:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lewis Hyatt X-Patchwork-Id: 133596 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b824:0:b0:3f2:4152:657d with SMTP id z4csp42590vqi; Wed, 9 Aug 2023 15:21:07 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFw4iRdE4puLLHgMDzhm9ViQ2hrbyHWqEJaUeKl2Oe3VBvWxhMGuOIxhn5kDR3Neq9Y0vmm X-Received: by 2002:a2e:8686:0:b0:2b6:d63d:cc1e with SMTP id l6-20020a2e8686000000b002b6d63dcc1emr378591lji.51.1691619667064; Wed, 09 Aug 2023 15:21:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1691619667; cv=none; d=google.com; s=arc-20160816; b=KlFnia87SacEL5+3c9zsitNdqeMi5Rg1vsojBzwRe0qnxcjmIZO+dT3PmV+1w485pX ba8Ocb3mlSl5iBAaJ/1+Czh60X7Hy4IPGH4HKj0aHZjXw/jFT71gdj0ut2laBnq7uDKr zTlaQPm6j69xhEbNE3kBI8aw0ZwerDR+PsrU2TPwzFcfEOgru/DyUfdGO9fuPGpYdFmE ksa5Fm+YP8G/+ZmTCuNisacgyaHamm9Dg1oyvxzBluotumkRHHEpo7ENpPiRz3psxid/ I0Mukteqd8tPY2UUuT8emcfWnkrO5mzuui0uajpTqhKEL0O1ZFLlzg+CyMfvhDn1uvAs XzYA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:dmarc-filter:delivered-to :dkim-signature:dkim-filter; bh=bEkNGogw2XTZuv7q7QmNEv1OGxrxaXQ4yvVqLv9WS50=; fh=hLxXrzU+VDBolomQxjoi9c6yn4Oij2Jaf7BaYMHGh24=; b=AdR93BS5C3Ju66t10j/LxeWt6gcHD2W4vRyoE5SRdptvVEXp2S98TcqLWQrMc+fpaH 9Ltw6LtaRzlpkcqUTk9z63+dOkeno3haMQjq5iUeMa8xans/jBnA3lGQtQRwr0mfJi7C 5dcfRWkIZjPA/D4qGcgu33TPxNdMKDVP3DLAKmqjnFz5pVjGpYWHmDG/C4DtmEEaFb1f NbhnFhUGXh2oDScwrU2z9aK3qRRfAbfe8Eh2YardirURP55Gqvj2LUmAEbPCnnFAljce 7Zv+0FCxchZmOd41i2mratKcP/AhFM2hB66t80Ahbx+O+3K32+ihi/mESJrUmCLD0Egr JYhA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=IRV3irAJ; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id w10-20020a17090652ca00b0099cac979982si136803ejn.1047.2023.08.09.15.21.06 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Aug 2023 15:21:07 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=IRV3irAJ; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D77F93830B6F for ; Wed, 9 Aug 2023 22:18:36 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D77F93830B6F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1691619516; bh=bEkNGogw2XTZuv7q7QmNEv1OGxrxaXQ4yvVqLv9WS50=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=IRV3irAJu0lORe7WJpLEwPZcGTEI/O/DgwbvUl7e4xMRH+/j2TXRKEwr3nvj8tGSH yKHeQpQBwM6aSVokXwth/5aT36wkkDCW6EWNzpakwleXIUPi7V5Mh1ZWHncRuAzwv1 zUefeijx1pLk6IDKvwDW4c9/UQEwpCRe4NM9ZoUs= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-qt1-x82a.google.com (mail-qt1-x82a.google.com [IPv6:2607:f8b0:4864:20::82a]) by sourceware.org (Postfix) with ESMTPS id 443CD3857C41 for ; Wed, 9 Aug 2023 22:14:53 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 443CD3857C41 Received: by mail-qt1-x82a.google.com with SMTP id d75a77b69052e-4039f7e1d3aso1541841cf.0 for ; Wed, 09 Aug 2023 15:14:53 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1691619292; x=1692224092; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=bEkNGogw2XTZuv7q7QmNEv1OGxrxaXQ4yvVqLv9WS50=; b=X4GSxyrFXbXUzaK6Ra+sTSx2hjkneqegoDaXTw+L0ZMwiNBQisAcT6/027camTGAKk s7s1rx0PMujVU8FRBvIZ/DrsaTmr1zY4zjlcoYFYM9BCG9mUJDn+F/GLk3B+G3k0CLe6 +1m+fV1hjJ2Yaj+mLklulId80Oasw8H5av5IRMsmXffvYNSes3H9QfmjdYm3dsof1qbi dfOYJbQ6ZlIyopf1e2VVWz0E64gVwQm4fret7+ODi3oMoJBKgzH75UVM7XkKGENeX2EK 4g0aBmyybofmuzKbGt/jgO6AHANk6JU6gZPMwCsRJYFS63mb6yHUsHggDqGpwbn1z8sy m9Vw== X-Gm-Message-State: AOJu0YyMGckk5bz5VZQmBk5Zho0sVWi+U6ot+DlaIp6fprpLk0jKVYzG HKGpksaAfxT4u0fXQo8EZmg/X3QbJVA= X-Received: by 2002:ac8:59c3:0:b0:403:a9aa:56d6 with SMTP id f3-20020ac859c3000000b00403a9aa56d6mr768237qtf.58.1691619291946; Wed, 09 Aug 2023 15:14:51 -0700 (PDT) Received: from localhost.localdomain (96-67-140-173-static.hfc.comcastbusiness.net. [96.67.140.173]) by smtp.gmail.com with ESMTPSA id ce11-20020a05622a41cb00b0040fef71dc1esm46334qtb.10.2023.08.09.15.14.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Aug 2023 15:14:51 -0700 (PDT) To: gcc-patches@gcc.gnu.org Cc: David Malcolm , Lewis Hyatt Subject: [PATCH v4 7/8] diagnostics: libcpp: Assign real locations to the tokens inside _Pragma strings Date: Wed, 9 Aug 2023 18:14:13 -0400 Message-Id: <20230809221414.2849878-8-lhyatt@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230809221414.2849878-1-lhyatt@gmail.com> References: <20230809221414.2849878-1-lhyatt@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-3038.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Lewis Hyatt via Gcc-patches From: Lewis Hyatt Reply-To: Lewis Hyatt Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1773791784130563323 X-GMAIL-MSGID: 1773791784130563323 Currently, the tokens obtained from a destringified _Pragma string do not get assigned proper locations while they are being lexed. After the tokens have been obtained, they are reassigned the same location as the _Pragma token, which is sufficient to make things like _Pragma("GCC diagnostic ignored...") operate correctly, but this still results in inferior diagnostics, since the diagnostics do not point to the problematic tokens. Further, if a diagnostic is issued by libcpp during the lexing of the tokens, as opposed to being issued by the frontend during the processing of the pragma, then the patched-up location is not yet in place, and the user rather sees an invalid location that is near to the location of the _Pragma string in some cases, or potentially very far away, depending on the macro expansion history. For example: ===== _Pragma("GCC diagnostic ignored \"oops") ===== produces the diagnostic: file.cpp:1:24: warning: missing terminating " character 1 | _Pragma("GCC diagnostic ignored \"oops") | ^ with the caret in a nonsensical location, while this one: ===== #define S "GCC diagnostic ignored \"oops" _Pragma(S) ===== produces: file.cpp:2:24: warning: missing terminating " character 2 | _Pragma(S) | ^ with both the caret in a nonsensical location, and the actual relevant context completely absent. Fix this by assigning proper locations using the new LC_GEN type of linemap. Now the tokens are given locations inside a generated content buffer, and the macro expansion stack is modified to be aware that these tokens logically belong to the "expansion" of the _Pragma directive. For the above examples we now output: ====== In buffer generated from file.cpp:1: :1:24: warning: missing terminating " character 1 | GCC diagnostic ignored "oops | ^ file.cpp:1:1: note: in <_Pragma directive> 1 | _Pragma("GCC diagnostic ignored \"oops") | ^~~~~~~ ====== and ====== :1:24: warning: missing terminating " character 1 | GCC diagnostic ignored "oops | ^ file.cpp:2:1: note: in <_Pragma directive> 2 | _Pragma(S) | ^~~~~~~ ====== So that carets are pointing to something meaningful and all relevant context appears in the diagnostic. For the second example, it would be nice if the macro expansion also output "in expansion of macro S", however doing that for a general case of macro expansions makes the logic very complicated, since it has to be done after the fact when the macro maps have already been constructed. It doesn't seem worth it for this case, given that the _Pragma string has already been output once on the first line. gcc/ChangeLog: * tree-diagnostic.cc (maybe_unwind_expanded_macro_loc): Add awareness of _Pragma directive to the macro expansion trace. libcpp/ChangeLog: * directives.cc (get_token_no_padding): Add argument to receive the virtual location of the token. (get__Pragma_string): Likewise. (do_pragma): Set pfile->directive_result->src_loc properly, it should not be a virtual location. (destringize_and_run): Update to provide proper locations for the _Pragma string tokens. Support raw strings. (_cpp_do__Pragma): Adapt to changes to the helper functions. * errors.cc (cpp_diagnostic_at): Support cpp_reader::diagnostic_rebase_loc. (cpp_diagnostic_with_line): Likewise. * include/line-map.h (class rich_location): Add new member forget_cached_expanded_locations(). * internal.h (struct _cpp__Pragma_state): Define new struct. (_cpp_rebase_diagnostic_location): Declare new function. (struct cpp_reader): Add diagnostic_rebase_loc member. (_cpp_push__Pragma_token_context): Declare new function. (_cpp_do__Pragma): Adjust prototype. * macro.cc (pragma_str): New static var. (builtin_macro): Adapt to new implementation of _Pragma processing. (_cpp_pop_context): Fix the logic for resetting pfile->top_most_macro_node, which previously was never triggered, although the error seems to have been harmless. (_cpp_push__Pragma_token_context): New function. (_cpp_rebase_diagnostic_location): New function. gcc/c-family/ChangeLog: * c-ppoutput.cc (token_streamer::stream): Pass the virtual location of the _Pragma token to maybe_print_line(), not the spelling location. libgomp/ChangeLog: * testsuite/libgomp.oacc-c-c++-common/reduction-5.c: Adjust for new macro tracking output for _Pragma directives. * testsuite/libgomp.oacc-c-c++-common/vred2d-128.c: Likewise. gcc/testsuite/ChangeLog: * c-c++-common/cpp/diagnostic-pragma-1.c: Adjust for new macro tracking output for _Pragma directives. * c-c++-common/cpp/pr57580.c: Likewise. * c-c++-common/gomp/pragma-3.c: Likewise. * c-c++-common/gomp/pragma-5.c: Likewise. * g++.dg/pch/operator-1.C: Likewise. * gcc.dg/cpp/pr28165.c: Likewise. * gcc.dg/cpp/pr35322.c: Likewise. * gcc.dg/dfp/pragma-float-const-decimal64-4.c: Likewise. * gcc.dg/dfp/pragma-float-const-decimal64-5.c: Likewise. * gcc.dg/dfp/pragma-float-const-decimal64-6.c: Likewise. * gcc.dg/gomp/macro-4.c: Likewise. * gcc.dg/pragma-message.c: Likewise. * c-c++-common/pragma-diag-17.c: New test. * c-c++-common/pragma-diag-18.c: New test. * g++.dg/cpp/pragma-raw-string.C: New test. * g++.dg/pch/LC_GEN-maps.C: New test. * g++.dg/pch/LC_GEN-maps.Hs: New test. * lib/prune.exp: Support pruning new _Pragma include trace. --- gcc/c-family/c-ppoutput.cc | 2 +- .../c-c++-common/cpp/diagnostic-pragma-1.c | 1 + gcc/testsuite/c-c++-common/cpp/pr57580.c | 2 +- gcc/testsuite/c-c++-common/gomp/pragma-3.c | 3 +- gcc/testsuite/c-c++-common/gomp/pragma-5.c | 3 +- gcc/testsuite/c-c++-common/pragma-diag-17.c | 35 +++ gcc/testsuite/c-c++-common/pragma-diag-18.c | 18 ++ gcc/testsuite/g++.dg/cpp/pragma-raw-string.C | 16 + gcc/testsuite/g++.dg/pch/LC_GEN-maps.C | 20 ++ gcc/testsuite/g++.dg/pch/LC_GEN-maps.Hs | 5 + gcc/testsuite/g++.dg/pch/operator-1.C | 1 + gcc/testsuite/gcc.dg/cpp/pr28165.c | 1 + gcc/testsuite/gcc.dg/cpp/pr35322.c | 1 + .../dfp/pragma-float-const-decimal64-4.c | 1 + .../dfp/pragma-float-const-decimal64-5.c | 2 +- .../dfp/pragma-float-const-decimal64-6.c | 2 +- gcc/testsuite/gcc.dg/gomp/macro-4.c | 2 +- gcc/testsuite/gcc.dg/pragma-message.c | 3 +- gcc/testsuite/lib/prune.exp | 1 + gcc/tree-diagnostic.cc | 18 +- libcpp/directives.cc | 278 ++++++++++++------ libcpp/errors.cc | 16 +- libcpp/include/line-map.h | 1 + libcpp/internal.h | 32 +- libcpp/macro.cc | 126 +++++++- .../libgomp.oacc-c-c++-common/reduction-5.c | 3 +- .../libgomp.oacc-c-c++-common/vred2d-128.c | 40 ++- 27 files changed, 491 insertions(+), 142 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/pragma-diag-17.c create mode 100644 gcc/testsuite/c-c++-common/pragma-diag-18.c create mode 100644 gcc/testsuite/g++.dg/cpp/pragma-raw-string.C create mode 100644 gcc/testsuite/g++.dg/pch/LC_GEN-maps.C create mode 100644 gcc/testsuite/g++.dg/pch/LC_GEN-maps.Hs diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc index 4aa2bef2c0f..364bfe5ad43 100644 --- a/gcc/c-family/c-ppoutput.cc +++ b/gcc/c-family/c-ppoutput.cc @@ -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) diff --git a/gcc/testsuite/c-c++-common/cpp/diagnostic-pragma-1.c b/gcc/testsuite/c-c++-common/cpp/diagnostic-pragma-1.c index 9867c94a8dd..801c93935b8 100644 --- a/gcc/testsuite/c-c++-common/cpp/diagnostic-pragma-1.c +++ b/gcc/testsuite/c-c++-common/cpp/diagnostic-pragma-1.c @@ -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 } diff --git a/gcc/testsuite/c-c++-common/cpp/pr57580.c b/gcc/testsuite/c-c++-common/cpp/pr57580.c index e77462b20de..b0e54d876d6 100644 --- a/gcc/testsuite/c-c++-common/cpp/pr57580.c +++ b/gcc/testsuite/c-c++-common/cpp/pr57580.c @@ -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\")") \ diff --git a/gcc/testsuite/c-c++-common/gomp/pragma-3.c b/gcc/testsuite/c-c++-common/gomp/pragma-3.c index 3e1b2111c3d..e0cffb8aeea 100644 --- a/gcc/testsuite/c-c++-common/gomp/pragma-3.c +++ b/gcc/testsuite/c-c++-common/gomp/pragma-3.c @@ -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 } */ } diff --git a/gcc/testsuite/c-c++-common/gomp/pragma-5.c b/gcc/testsuite/c-c++-common/gomp/pragma-5.c index 173c25e803a..787a334882d 100644 --- a/gcc/testsuite/c-c++-common/gomp/pragma-5.c +++ b/gcc/testsuite/c-c++-common/gomp/pragma-5.c @@ -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 } */ } diff --git a/gcc/testsuite/c-c++-common/pragma-diag-17.c b/gcc/testsuite/c-c++-common/pragma-diag-17.c new file mode 100644 index 00000000000..b9539c9598b --- /dev/null +++ b/gcc/testsuite/c-c++-common/pragma-diag-17.c @@ -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 } */ diff --git a/gcc/testsuite/c-c++-common/pragma-diag-18.c b/gcc/testsuite/c-c++-common/pragma-diag-18.c new file mode 100644 index 00000000000..5de0fbcb8f1 --- /dev/null +++ b/gcc/testsuite/c-c++-common/pragma-diag-18.c @@ -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 } */ diff --git a/gcc/testsuite/g++.dg/cpp/pragma-raw-string.C b/gcc/testsuite/g++.dg/cpp/pragma-raw-string.C new file mode 100644 index 00000000000..5a495aadeec --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp/pragma-raw-string.C @@ -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} } */ diff --git a/gcc/testsuite/g++.dg/pch/LC_GEN-maps.C b/gcc/testsuite/g++.dg/pch/LC_GEN-maps.C new file mode 100644 index 00000000000..4ce241579fe --- /dev/null +++ b/gcc/testsuite/g++.dg/pch/LC_GEN-maps.C @@ -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])*[d]ata: this string should end up in the "PCH"(.|[\n\r])*} } */ diff --git a/gcc/testsuite/g++.dg/pch/LC_GEN-maps.Hs b/gcc/testsuite/g++.dg/pch/LC_GEN-maps.Hs new file mode 100644 index 00000000000..76eefa7d1ae --- /dev/null +++ b/gcc/testsuite/g++.dg/pch/LC_GEN-maps.Hs @@ -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\"") diff --git a/gcc/testsuite/g++.dg/pch/operator-1.C b/gcc/testsuite/g++.dg/pch/operator-1.C index 290b5f7ab21..bf1c8b07bdb 100644 --- a/gcc/testsuite/g++.dg/pch/operator-1.C +++ b/gcc/testsuite/g++.dg/pch/operator-1.C @@ -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" } */ diff --git a/gcc/testsuite/gcc.dg/cpp/pr28165.c b/gcc/testsuite/gcc.dg/cpp/pr28165.c index 71c7c1dba46..3e5e49ffa01 100644 --- a/gcc/testsuite/gcc.dg/cpp/pr28165.c +++ b/gcc/testsuite/gcc.dg/cpp/pr28165.c @@ -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" } */ diff --git a/gcc/testsuite/gcc.dg/cpp/pr35322.c b/gcc/testsuite/gcc.dg/cpp/pr35322.c index 1af9605eac6..5bd5f69b73d 100644 --- a/gcc/testsuite/gcc.dg/cpp/pr35322.c +++ b/gcc/testsuite/gcc.dg/cpp/pr35322.c @@ -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" } */ diff --git a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-4.c b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-4.c index af0398daf79..42fc28a4384 100644 --- a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-4.c +++ b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-4.c @@ -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). diff --git a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-5.c b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-5.c index 75e9525dda0..3aefede7b5d 100644 --- a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-5.c +++ b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-5.c @@ -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). diff --git a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-6.c b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-6.c index 03c1715bee6..6d70ce2bb8d 100644 --- a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-6.c +++ b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-6.c @@ -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). diff --git a/gcc/testsuite/gcc.dg/gomp/macro-4.c b/gcc/testsuite/gcc.dg/gomp/macro-4.c index a4ed9a3980a..c6817d40125 100644 --- a/gcc/testsuite/gcc.dg/gomp/macro-4.c +++ b/gcc/testsuite/gcc.dg/gomp/macro-4.c @@ -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") diff --git a/gcc/testsuite/gcc.dg/pragma-message.c b/gcc/testsuite/gcc.dg/pragma-message.c index 1b7cf09de0a..72fb0da6f44 100644 --- a/gcc/testsuite/gcc.dg/pragma-message.c +++ b/gcc/testsuite/gcc.dg/pragma-message.c @@ -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") diff --git a/gcc/testsuite/lib/prune.exp b/gcc/testsuite/lib/prune.exp index 8d37b24e59b..02ebf8b30d9 100644 --- a/gcc/testsuite/lib/prune.exp +++ b/gcc/testsuite/lib/prune.exp @@ -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 diff --git a/gcc/tree-diagnostic.cc b/gcc/tree-diagnostic.cc index 731e3559cd8..fd2773f3d8a 100644 --- a/gcc/tree-diagnostic.cc +++ b/gcc/tree-diagnostic.cc @@ -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); } } diff --git a/libcpp/directives.cc b/libcpp/directives.cc index dfd782b3fca..d2d83e6dc83 100644 --- a/libcpp/directives.cc +++ b/libcpp/directives.cc @@ -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 *); @@ -1504,14 +1504,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) { @@ -1537,7 +1535,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; @@ -1830,11 +1828,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; } @@ -1843,7 +1841,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; @@ -1854,7 +1852,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 @@ -1874,55 +1872,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) @@ -1931,85 +1979,127 @@ 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) { /* Make sure we don't invalidate the string token, if the closing parenthesis ended up on a different line. */ ++pfile->keep_tokens; - const cpp_token *string = get__Pragma_string (pfile); + pstate->string_tok = get__Pragma_string (pfile, &pstate->string_loc); --pfile->keep_tokens; pfile->directive_result.type = CPP_PADDING; - - if (string) + 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, diff --git a/libcpp/errors.cc b/libcpp/errors.cc index 3269d076af2..54c1c282540 100644 --- a/libcpp/errors.cc +++ b/libcpp/errors.cc @@ -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. */ diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h index 76617fe6129..ae32584c264 100644 --- a/libcpp/include/line-map.h +++ b/libcpp/include/line-map.h @@ -1812,6 +1812,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); diff --git a/libcpp/internal.h b/libcpp/internal.h index 8b74d10c1a3..b6118d7128b 100644 --- a/libcpp/internal.h +++ b/libcpp/internal.h @@ -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 *, diff --git a/libcpp/macro.cc b/libcpp/macro.cc index dada8fea835..864e7dabc38 100644 --- a/libcpp/macro.cc +++ b/libcpp/macro.cc @@ -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, @@ -4137,3 +4164,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 (); +} diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c index ddccfe89e73..f518915492d 100644 --- a/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c +++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c @@ -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. */ diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c index 84e6d51670b..bd2567d96f8 100644 --- a/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c +++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c @@ -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. */