@@ -2840,6 +2840,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
$(srcdir)/reg-stack.cc $(srcdir)/cfgrtl.cc \
$(srcdir)/stor-layout.cc \
$(srcdir)/stringpool.cc $(srcdir)/tree.cc $(srcdir)/varasm.cc \
+ $(srcdir)/tree-diagnostic.cc \
$(srcdir)/gimple.h \
$(srcdir)/gimple-ssa.h \
$(srcdir)/tree-ssanames.cc $(srcdir)/tree-eh.cc $(srcdir)/tree-ssa-address.cc \
@@ -714,6 +714,11 @@ cp_lexer_new_main (void)
cp_parser_initial_pragma (&token);
c_common_no_more_pch ();
+ /* Remember the current state of diagnostics classifications, so we can
+ reset it after preprocessing. */
+ auto &classifier = global_dc->get_classifier ();
+ const auto class_state = classifier.get_state ();
+
cp_lexer *lexer = cp_lexer_alloc ();
/* Put the first token in the buffer. */
cp_token *tok = lexer->buffer->quick_push (token);
@@ -772,6 +777,12 @@ cp_lexer_new_main (void)
process them again at the correct time as needed. */
c_reset_target_pragmas ();
+ /* Reset the state of any diagnostic pragmas to the initial state, so that
+ e.g. an unbalanced set of push/pop pragmas would not be observable in
+ a precompiled header. */
+ classifier.restore_state (class_state);
+ classifier.free_state (class_state);
+
gcc_assert (!lexer->next_token->purged_p);
return lexer;
}
@@ -156,6 +156,8 @@ diagnostic_option_classifier::init (int n_opts)
m_classify_diagnostic = XNEWVEC (diagnostic_t, n_opts);
for (int i = 0; i < n_opts; i++)
m_classify_diagnostic[i] = DK_UNSPECIFIED;
+ m_classification_history = nullptr;
+ m_n_classification_history = 0;
m_push_list = nullptr;
m_n_push = 0;
}
@@ -165,7 +167,11 @@ diagnostic_option_classifier::fini ()
{
XDELETEVEC (m_classify_diagnostic);
m_classify_diagnostic = nullptr;
- free (m_push_list);
+ XDELETEVEC (m_classification_history);
+ m_classification_history = nullptr;
+ m_n_classification_history = 0;
+ XDELETEVEC (m_push_list);
+ m_push_list = nullptr;
m_n_push = 0;
}
@@ -1123,11 +1129,7 @@ classify_diagnostic (const diagnostic_context *context,
/* Record the command-line status, so we can reset it back on DK_POP. */
if (old_kind == DK_UNSPECIFIED)
{
- old_kind = !context->m_option_enabled (option_index,
- context->m_lang_mask,
- context->m_option_state)
- ? DK_IGNORED : (context->warning_as_error_requested_p ()
- ? DK_ERROR : DK_WARNING);
+ old_kind = context->get_original_option_classification (option_index);
m_classify_diagnostic[option_index] = old_kind;
}
@@ -2471,6 +2473,18 @@ set_text_art_charset (enum diagnostic_text_art_charset charset)
}
}
+/* Return the original state of an option prior to any reclassification that may
+ have been done via diagnostic pragmas. */
+diagnostic_t
+diagnostic_context::get_original_option_classification (int option_index) const
+{
+ if (!m_option_enabled)
+ return DK_UNSPECIFIED;
+ if (!m_option_enabled (option_index, m_lang_mask, m_option_state))
+ return DK_IGNORED;
+ return warning_as_error_requested_p () ? DK_ERROR : DK_WARNING;
+}
+
/* class simple_diagnostic_path : public diagnostic_path. */
simple_diagnostic_path::simple_diagnostic_path (pretty_printer *event_pp)
@@ -267,6 +267,20 @@ public:
diagnostic_t
update_effective_level_from_pragmas (diagnostic_info *diagnostic) const;
+ /* See comments in class diagnostic_context regarding PCH support. */
+ struct state;
+ void save_state_to (state *st, bool is_ggc) const;
+ void restore_from_pch (const diagnostic_context *context);
+
+ /* These are helper routines for PCH support, but they may also be useful on
+ their own, e.g. to save and restore the state of all classifications. The
+ returned state should be passed to free_state () when it is no longer
+ needed; otherwise it may be passed to restore_state () as many times as
+ needed. */
+ state * get_state () const;
+ bool restore_state (const state *st);
+ static void free_state (state *st);
+
private:
/* Each time a diagnostic's classification is changed with a pragma,
we record the change and the location of the change in an array of
@@ -401,6 +415,8 @@ public:
where);
}
+ diagnostic_t get_original_option_classification (int option_index) const;
+
void push_diagnostics (location_t where ATTRIBUTE_UNUSED)
{
m_option_classifier.push ();
@@ -463,6 +479,18 @@ public:
return m_file_cache;
}
+ const diagnostic_option_classifier &
+ get_classifier () const
+ {
+ return m_option_classifier;
+ }
+
+ diagnostic_option_classifier &
+ get_classifier ()
+ {
+ return m_option_classifier;
+ }
+
edit_context *get_edit_context () const
{
return m_edit_context_ptr;
@@ -666,6 +694,12 @@ public:
/* The size of the tabstop for tab expansion. */
int m_tabstop;
+ /* Support to remember a subset of the configuration in a PCH. Implemented in
+ tree-diagnostic.cc, since diagnostic.cc may be used in contexts (via
+ libcommon.a) where GGC routines are not available. */
+ void save_to_pch () const;
+ void restore_from_pch ();
+
private:
/* How should non-ASCII/non-printable bytes be escaped when
a diagnostic suggests escaping the source code on output. */
@@ -25,7 +25,7 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
#include "timevar.h"
-#include "diagnostic-core.h"
+#include "diagnostic.h"
#include "ggc-internal.h"
#include "hosthooks.h"
#include "plugin.h"
@@ -520,6 +520,7 @@ gt_pch_save (FILE *f)
const size_t mmap_offset_alignment = host_hooks.gt_pch_alloc_granularity ();
gt_pch_save_stringpool ();
+ global_dc->save_to_pch ();
timevar_push (TV_PCH_PTR_REALLOC);
saving_htab = new hash_table<saving_hasher> (50000);
@@ -916,6 +917,8 @@ gt_pch_restore (FILE *f)
/* Barring corruption of the PCH file, the restored line table should be
complete and usable. */
line_table = new_line_table;
+
+ global_dc->restore_from_pch ();
}
/* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS when mmap is not present.
@@ -379,3 +379,154 @@ tree_diagnostics_defaults (diagnostic_context *context)
context->set_set_locations_callback (set_inlining_locations);
context->set_client_data_hooks (make_compiler_data_hooks ());
}
+
+/* Plumbing to remember diagnostic classification history in a PCH, so that
+ `#pragma GCC diagnostic' directives from the PCH will take effect after
+ restoring the PCH. */
+
+struct GTY (()) diagnostic_option_classifier::state
+{
+ int n_opts;
+ int n_classification_history;
+ int n_push;
+ void * GTY((atomic)) classification_history;
+ int * GTY((atomic)) push_list;
+};
+
+struct GTY (()) diagnostic_pch_data
+{
+ diagnostic_option_classifier::state class_state;
+};
+
+static GTY(()) diagnostic_pch_data *pch;
+
+void
+diagnostic_context::save_to_pch () const
+{
+ pch = ggc_cleared_alloc<diagnostic_pch_data> ();
+ m_option_classifier.save_state_to (&pch->class_state, true);
+}
+
+void
+diagnostic_context::restore_from_pch ()
+{
+ if (!pch)
+ return;
+ m_option_classifier.restore_from_pch (this);
+ pch = nullptr;
+}
+
+void
+diagnostic_option_classifier::save_state_to (state *st, bool is_ggc) const
+{
+ st->n_opts = m_n_opts;
+
+#define DC_ALLOC(T, N) (is_ggc ? ggc_vec_alloc<T> (N) : XNEWVEC (T, N))
+
+ if (m_n_classification_history)
+ {
+ using dcc = diagnostic_classification_change_t;
+ st->n_classification_history = m_n_classification_history;
+ st->classification_history = DC_ALLOC (dcc, m_n_classification_history);
+ memcpy (st->classification_history, m_classification_history,
+ m_n_classification_history * sizeof (dcc));
+ }
+
+ if (m_n_push)
+ {
+ st->n_push = m_n_push;
+ st->push_list = DC_ALLOC (int, m_n_push);
+ memcpy (st->push_list, m_push_list, m_n_push * sizeof (int));
+ }
+
+#undef DC_ALLOC
+
+}
+
+diagnostic_option_classifier::state *
+diagnostic_option_classifier::get_state () const
+{
+ const auto st = XCNEW (diagnostic_option_classifier::state);
+ save_state_to (st, false);
+ return st;
+}
+
+bool
+diagnostic_option_classifier::restore_state (const state *st)
+{
+ if (st->n_opts != m_n_opts)
+ return false;
+
+ m_n_classification_history = st->n_classification_history;
+ if (m_n_classification_history)
+ {
+ using dcc = diagnostic_classification_change_t;
+ m_classification_history = XRESIZEVEC (dcc, m_classification_history,
+ m_n_classification_history);
+ memcpy (m_classification_history, st->classification_history,
+ m_n_classification_history * sizeof (dcc));
+ }
+
+ m_n_push = st->n_push;
+ if (m_n_push)
+ {
+ m_push_list = XRESIZEVEC (int, m_push_list, m_n_push);
+ memcpy (m_push_list, st->push_list, m_n_push * sizeof (int));
+ }
+
+ return true;
+}
+
+void
+diagnostic_option_classifier::
+restore_from_pch (const diagnostic_context *context)
+{
+ /* N.B. We do not need to worry about preserving whatever exists already in
+ this object, because we would have declined to read a PCH if a `#pragma GCC
+ diagnostic' had been processed already. If it should be supported in the
+ future, then any location_t values stored in the classification_history are
+ invalid (being valid only for the old line_table, which has now been
+ replaced by a new one from the PCH), so we would need to go through and
+ create new location_t values pointing to some valid location in the new
+ line_table. */
+ if (!restore_state (&pch->class_state))
+ return;
+
+ /* m_classify_diagnostic is used to record the state of each option as
+ specified on the command line prior to processing any diagnostic pragmas.
+ This makes it possible for the outermost `#pragma GCC diagnostic pop' to
+ restore that initial state. We do not automatically populate
+ m_classify_diagnostic for all options, rather we make the necessary memo
+ about the initial state only when we change the state via a diagnostic
+ pragma. So if the state has been changed not by a diagnostic pragma in
+ *this* translation unit, but rather by a diagnostic pragma recorded in the
+ PCH, we need to record the initial state now so that we can pop to it
+ later. (It is permitted to have an unbalanced `#pragma GCC diagnostic
+ push' in a header--be it PCH or regular--and then to have the corresponding
+ `#pragma GCC diagnostic pop' say in another header, or in the main source
+ file.) Since the state to which a pop directive needs to move is the
+ initial state from the current compilation, not the initial state from the
+ compilation of the PCH file, we can't read this data from the PCH but
+ rather need to figure it out now. */
+
+ for (int i = 0; i != m_n_classification_history; ++i)
+ {
+ const auto &hist = m_classification_history[i];
+ if (hist.kind != (int) DK_POP)
+ {
+ auto &c = m_classify_diagnostic[hist.option];
+ if (c == DK_UNSPECIFIED)
+ c = context->get_original_option_classification (hist.option);
+ }
+ }
+}
+
+void
+diagnostic_option_classifier::free_state (state *st)
+{
+ XDELETEVEC (st->classification_history);
+ XDELETEVEC (st->push_list);
+ XDELETE (st);
+}
+
+#include "gt-tree-diagnostic.h"
new file mode 100644
@@ -0,0 +1,19 @@
+/* PR pch/64117 */
+/* { dg-additional-options "-Werror=uninitialized -ftrack-macro-expansion=0" } */
+#include "pragma-diagnostic-1.H"
+#define UNINIT1(x) void f ## x () { int i; int j = i; }
+#define UNINIT2(x) UNINIT1 (x) _Pragma("GCC diagnostic pop")
+#define UNINIT UNINIT2(__LINE__)
+
+/* Diagnostic should be ignored in the innermost two contexts. */
+UNINIT /* { dg-bogus "uninitialized" } */
+UNINIT /* { dg-bogus "uninitialized" } */
+
+/* Diagnostic should be a warning now. */
+UNINIT /* { dg-warning "uninitialized" } */
+
+/* Make sure the final pop took it to an error, as requested by -Werror,
+ not to a warning, as was the case when the PCH was compiled. */
+UNINIT /* { dg-error "uninitialized" } */
+
+/* { dg-regexp {[^[:blank:]]*: some warnings being treated as errors} } */
new file mode 100644
@@ -0,0 +1,6 @@
+/* PR pch/64117 */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wuninitialized"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#pragma GCC diagnostic push
new file mode 100644
@@ -0,0 +1,19 @@
+/* PR pch/64117 */
+/* { dg-additional-options "-Werror -Wuninitialized -ftrack-macro-expansion=0" } */
+#include "pragma-diagnostic-2.H"
+#define UNINIT1(x) void f ## x () { int i; int j = i; }
+#define UNINIT2(x) UNINIT1 (x) _Pragma("GCC diagnostic pop")
+#define UNINIT UNINIT2(__LINE__)
+
+/* Diagnostic should be ignored in the innermost two contexts. */
+UNINIT /* { dg-bogus "uninitialized" } */
+UNINIT /* { dg-bogus "uninitialized" } */
+
+/* Diagnostic should be a warning now. */
+UNINIT /* { dg-warning "uninitialized" } */
+
+/* Make sure the final pop took it to an error, as requested by -Werror,
+ not to a warning, as was the case when the PCH was compiled. */
+UNINIT /* { dg-error "uninitialized" } */
+
+/* { dg-regexp {[^[:blank:]]*: all warnings being treated as errors} } */
new file mode 100644
@@ -0,0 +1,6 @@
+/* PR pch/64117 */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wuninitialized"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#pragma GCC diagnostic push
new file mode 100644
@@ -0,0 +1,5 @@
+/* PR pch/64117 */
+/* { dg-additional-options "-Wdate-time" } */
+#include "pragma-diagnostic-3.H"
+#pragma GCC diagnostic pop
+const char *s2 = __DATE__; /* { dg-warning "-Wdate-time" } */
new file mode 100644
@@ -0,0 +1,4 @@
+/* PR pch/64117 */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdate-time"
+const char *s1 = __DATE__;
new file mode 100644
@@ -0,0 +1,17 @@
+/* PR pch/64117 */
+/* { dg-additional-options "-Wpragmas -Wuninitialized" } */
+#include "pragma-diagnostic-4.H"
+#pragma GCC diagnostic ignored "oops" /* { dg-bogus "pragmas" } */
+void f1 ()
+{
+ int i;
+ int j = i; /* { dg-bogus "uninitialized" } */
+}
+#pragma GCC diagnostic pop
+_Pragma("GCC diagnostic ignored \"oops\"") /* { dg-error "pragmas" } */
+void f2 ()
+{
+ int i;
+ int j = i; /* { dg-error "uninitialized" } */
+}
+/* { dg-regexp {[^[:blank:]]*: some warnings being treated as errors} } */
new file mode 100644
@@ -0,0 +1,6 @@
+/* PR pch/64117 */
+#pragma GCC diagnostic error "-Wpragmas"
+#pragma GCC diagnostic error "-Wuninitialized"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Wuninitialized"
new file mode 100644
@@ -0,0 +1,5 @@
+/* PR pch/64117 */
+/* { dg-additional-options "-Weffc++" } */
+#include "pragma-diagnostic-5.H"
+Struct<int> global;
+/* { dg-bogus "should be initialized in the member initialization list" "" { target *-*-* } 6 } */
new file mode 100644
@@ -0,0 +1,8 @@
+/* PR pch/64117 */
+#pragma GCC diagnostic ignored "-Weffc++"
+template<typename T>
+struct Struct {
+ T *pointer;
+ Struct() {}
+};
+#pragma GCC diagnostic error "-Weffc++"
new file mode 100644
@@ -0,0 +1,19 @@
+/* PR pch/64117 */
+/* { dg-additional-options "-Werror=uninitialized -ftrack-macro-expansion=0" } */
+#include "pragma-diagnostic-1.h"
+#define UNINIT1(x) void f ## x () { int i; int j = i; }
+#define UNINIT2(x) UNINIT1 (x) _Pragma("GCC diagnostic pop")
+#define UNINIT UNINIT2(__LINE__)
+
+/* Diagnostic should be ignored in the innermost two contexts. */
+UNINIT /* { dg-bogus "uninitialized" } */
+UNINIT /* { dg-bogus "uninitialized" } */
+
+/* Diagnostic should be a warning now. */
+UNINIT /* { dg-warning "uninitialized" } */
+
+/* Make sure the final pop took it to an error, as requested by -Werror,
+ not to a warning, as was the case when the PCH was compiled. */
+UNINIT /* { dg-error "uninitialized" } */
+
+/* { dg-regexp {[^[:blank:]]*: some warnings being treated as errors} } */
new file mode 100644
@@ -0,0 +1,6 @@
+/* PR pch/64117 */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wuninitialized"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#pragma GCC diagnostic push
new file mode 100644
@@ -0,0 +1,19 @@
+/* PR pch/64117 */
+/* { dg-additional-options "-Werror -Wuninitialized -ftrack-macro-expansion=0" } */
+#include "pragma-diagnostic-2.h"
+#define UNINIT1(x) void f ## x () { int i; int j = i; }
+#define UNINIT2(x) UNINIT1 (x) _Pragma("GCC diagnostic pop")
+#define UNINIT UNINIT2(__LINE__)
+
+/* Diagnostic should be ignored in the innermost two contexts. */
+UNINIT /* { dg-bogus "uninitialized" } */
+UNINIT /* { dg-bogus "uninitialized" } */
+
+/* Diagnostic should be a warning now. */
+UNINIT /* { dg-warning "uninitialized" } */
+
+/* Make sure the final pop took it to an error, as requested by -Werror,
+ not to a warning, as was the case when the PCH was compiled. */
+UNINIT /* { dg-error "uninitialized" } */
+
+/* { dg-regexp {[^[:blank:]]*: all warnings being treated as errors} } */
new file mode 100644
@@ -0,0 +1,6 @@
+/* PR pch/64117 */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wuninitialized"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#pragma GCC diagnostic push
new file mode 100644
@@ -0,0 +1,5 @@
+/* PR pch/64117 */
+/* { dg-additional-options "-Wdate-time" } */
+#include "pragma-diagnostic-3.h"
+#pragma GCC diagnostic pop
+const char *s2 = __DATE__; /* { dg-warning "-Wdate-time" } */
new file mode 100644
@@ -0,0 +1,4 @@
+/* PR pch/64117 */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdate-time"
+const char *s1 = __DATE__;
new file mode 100644
@@ -0,0 +1,17 @@
+/* PR pch/64117 */
+/* { dg-additional-options "-Wpragmas -Wuninitialized" } */
+#include "pragma-diagnostic-4.h"
+#pragma GCC diagnostic ignored "oops" /* { dg-bogus "pragmas" } */
+void f1 ()
+{
+ int i;
+ int j = i; /* { dg-bogus "uninitialized" } */
+}
+#pragma GCC diagnostic pop
+_Pragma("GCC diagnostic ignored \"oops\"") /* { dg-error "pragmas" } */
+void f2 ()
+{
+ int i;
+ int j = i; /* { dg-error "uninitialized" } */
+}
+/* { dg-regexp {[^[:blank:]]*: some warnings being treated as errors} } */
new file mode 100644
@@ -0,0 +1,6 @@
+/* PR pch/64117 */
+#pragma GCC diagnostic error "-Wpragmas"
+#pragma GCC diagnostic error "-Wuninitialized"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Wuninitialized"