https://gcc.gnu.org/g:a5933118f34a97463ffa765744eedf27b9c2c68f

commit r16-1539-ga5933118f34a97463ffa765744eedf27b9c2c68f
Author: Jason Merrill <ja...@redhat.com>
Date:   Wed Nov 20 16:20:52 2024 +0100

    c++: modules and #pragma diagnostic
    
    To respect the #pragma diagnostic lines in libstdc++ headers when compiling
    with module std, we need to represent them in the module.
    
    I think it's reasonable to give serializers direct access to the underlying
    data, as here with get_classification_history.  This is a different approach
    from how Jakub made PCH streaming members of diagnostic_option_classifier,
    but it seems to me that modules handling belongs in module.cc.
    
    libcpp/ChangeLog:
    
            * line-map.cc (linemap_location_from_module_p): Add.
            * include/line-map.h: Declare it.
    
    gcc/ChangeLog:
    
            * diagnostic.h (diagnostic_option_classifier): Friend
            diagnostic_context.
            (diagnostic_context::get_classification_history): New.
    
    gcc/cp/ChangeLog:
    
            * module.cc (module_state::write_diagnostic_classification): New.
            (module_state::write_begin): Call it.
            (module_state::read_diagnostic_classification): New.
            (module_state::read_initial): Call it.
            (dk_string, dump_dc_change): New.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/modules/warn-spec-3_a.C: New test.
            * g++.dg/modules/warn-spec-3_b.C: New test.
            * g++.dg/modules/warn-spec-3_c.C: New test.

Diff:
---
 gcc/diagnostic.h                             |  10 ++
 libcpp/include/line-map.h                    |   4 +
 gcc/cp/module.cc                             | 177 ++++++++++++++++++++++++++-
 gcc/testsuite/g++.dg/modules/warn-spec-3_a.C |  20 +++
 gcc/testsuite/g++.dg/modules/warn-spec-3_b.C |   7 ++
 gcc/testsuite/g++.dg/modules/warn-spec-3_c.C |  12 ++
 libcpp/line-map.cc                           |  11 ++
 7 files changed, 240 insertions(+), 1 deletion(-)

diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index cdd6f26ba2a3..b6cab0d52edf 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -307,6 +307,9 @@ private:
      diagnostic.  */
   vec<diagnostic_classification_change_t> m_classification_history;
 
+  /* For diagnostic_context::get_classification_history, declared later.  */
+  friend class diagnostic_context;
+
   /* For pragma push/pop.  */
   vec<int> m_push_list;
 };
@@ -830,6 +833,13 @@ public:
     m_abort_on_error = val;
   }
 
+  /* Accessor for use in serialization, e.g. by C++ modules.  */
+  auto &
+  get_classification_history ()
+  {
+    return m_option_classifier.m_classification_history;
+  }
+
 private:
   void error_recursion () ATTRIBUTE_NORETURN;
 
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index 83ebbde474df..21a59af22369 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -1111,6 +1111,10 @@ extern location_t linemap_module_loc
 extern void linemap_module_reparent
   (line_maps *, location_t loc, location_t new_parent);
 
+/* TRUE iff the location comes from a module import.  */
+extern bool linemap_location_from_module_p
+  (const line_maps *, location_t);
+
 /* Restore the linemap state such that the map at LWM-1 continues.
    Return start location of the new map.  */
 extern location_t linemap_module_restore
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index ad2acaf559fb..c99988da05b8 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -3879,6 +3879,10 @@ class GTY((chain_next ("%h.parent"), for_user)) 
module_state {
   void write_macro_maps (elf_out *to, range_t &, unsigned *crc_ptr);
   bool read_macro_maps (line_map_uint_t);
 
+  void write_diagnostic_classification (elf_out *, diagnostic_context *,
+                                       unsigned *);
+  bool read_diagnostic_classification (diagnostic_context *);
+
  private:
   void write_define (bytes_out &, const cpp_macro *);
   cpp_macro *read_define (bytes_in &, cpp_reader *) const;
@@ -18167,6 +18171,168 @@ module_state::write_ordinary_maps (elf_out *to, 
range_t &info,
   dump.outdent ();
 }
 
+/* Return the prefix to use for dumping a #pragma diagnostic change to DK.  */
+
+static const char *
+dk_string (diagnostic_t dk)
+{
+  gcc_assert (dk > DK_UNSPECIFIED && dk < DK_LAST_DIAGNOSTIC_KIND);
+  if (dk == DK_IGNORED)
+    /* diagnostic.def has an empty string for ignored.  */
+    return "ignored: ";
+  else
+    return get_diagnostic_kind_text (dk);
+}
+
+/* Dump one #pragma GCC diagnostic entry.  */
+
+static bool
+dump_dc_change (unsigned index, unsigned opt, diagnostic_t dk)
+{
+  if (dk == DK_POP)
+    return dump (" Index %u: pop from %d", index, opt);
+  else
+    return dump (" Index %u: %s%s", index, dk_string (dk),
+                cl_options[opt].opt_text);
+}
+
+/* Write out any #pragma GCC diagnostic info to the .dgc section.  */
+
+void
+module_state::write_diagnostic_classification (elf_out *to,
+                                              diagnostic_context *dc,
+                                              unsigned *crc_p)
+{
+  auto &changes = dc->get_classification_history ();
+
+  bytes_out sec (to);
+  if (sec.streaming_p ())
+    {
+      sec.begin ();
+      dump () && dump ("Writing diagnostic change locations");
+      dump.indent ();
+    }
+
+  unsigned len = changes.length ();
+
+  /* We don't want to write out any entries that came from one of our imports.
+     But then we need to adjust the total, and change DK_POP targets to match
+     the index in our actual output.  So remember how many lines we had skipped
+     at each step, where -1 means this line itself is skipped.  */
+  int skips = 0;
+  auto_vec<int> skips_at (len);
+  skips_at.safe_grow (len);
+
+  for (unsigned i = 0; i < len; ++i)
+    {
+      const auto &c = changes[i];
+      skips_at[i] = skips;
+      if (linemap_location_from_module_p (line_table, c.location))
+       {
+         ++skips;
+         skips_at[i] = -1;
+         continue;
+       }
+    }
+
+  if (sec.streaming_p ())
+    {
+      sec.u (len - skips);
+      dump () && dump ("Diagnostic changes: %u", len - skips);
+    }
+
+  for (unsigned i = 0; i < len; ++i)
+    {
+      if (skips_at[i] == -1)
+       continue;
+
+      const auto &c = changes[i];
+      write_location (sec, c.location);
+      if (sec.streaming_p ())
+       {
+         unsigned opt = c.option;
+         if (c.kind == DK_POP)
+           opt -= skips_at[opt];
+         sec.u (opt);
+         sec.u (c.kind);
+         dump () && dump_dc_change (i - skips_at[i], opt, c.kind);
+       }
+    }
+
+  if (sec.streaming_p ())
+    {
+      sec.end (to, to->name (MOD_SNAME_PFX ".dgc"), crc_p);
+      dump.outdent ();
+    }
+}
+
+/* Read any #pragma GCC diagnostic info from the .dgc section.  */
+
+bool
+module_state::read_diagnostic_classification (diagnostic_context *dc)
+{
+  bytes_in sec;
+
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".dgc"))
+    return false;
+
+  dump () && dump ("Reading diagnostic change locations");
+  dump.indent ();
+
+  unsigned len = sec.u ();
+  dump () && dump ("Diagnostic changes: %u", len);
+
+  auto &changes = dc->get_classification_history ();
+  int offset = changes.length ();
+  changes.reserve (len + 1);
+  for (unsigned i = 0; i < len; ++i)
+    {
+      location_t loc = read_location (sec);
+      int opt = sec.u ();
+      diagnostic_t kind = (diagnostic_t) sec.u ();
+      if (kind == DK_POP)
+       /* For a pop, opt is the 'changes' index to return to.  */
+       opt += offset;
+      changes.quick_push ({ loc, opt, kind });
+      dump () && dump_dc_change (changes.length () - 1, opt, kind);
+    }
+
+  /* Did the import pop all its diagnostic changes?  */
+  bool last_was_reset = (len == 0);
+  if (len)
+    for (int i = changes.length () - 1; ; --i)
+      {
+       gcc_checking_assert (i >= offset);
+
+       const auto &c = changes[i];
+       if (c.kind != DK_POP)
+         break;
+       else if (c.option == offset)
+         {
+           last_was_reset = true;
+           break;
+         }
+       else
+         /* As in update_effective_level_from_pragmas, the loop will decrement
+            i so we actually jump to c.option - 1.  */
+         i = c.option;
+      }
+  if (!last_was_reset)
+    {
+      /* It didn't, so add a pop at its last location to avoid affecting later
+        imports.  */
+      location_t last_loc = ordinary_locs.first + ordinary_locs.second - 1;
+      changes.quick_push ({ last_loc, offset, DK_POP });
+      dump () && dump (" Adding final pop from index %d", offset);
+    }
+
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+
+  return true;
+}
+
 void
 module_state::write_macro_maps (elf_out *to, range_t &info, unsigned *crc_p)
 {
@@ -19853,6 +20019,8 @@ module_state::write_begin (elf_out *to, cpp_reader 
*reader,
     }
   ool->qsort (ool_cmp);
 
+  write_diagnostic_classification (nullptr, global_dc, nullptr);
+
   vec<cpp_hashnode *> *macros = nullptr;
   if (is_header ())
     macros = prepare_macros (reader);
@@ -19998,7 +20166,10 @@ module_state::write_begin (elf_out *to, cpp_reader 
*reader,
 
   /* Write the line maps.  */
   if (config.ordinary_locs)
-    write_ordinary_maps (to, map_info, bool (config.num_partitions), &crc);
+    {
+      write_ordinary_maps (to, map_info, bool (config.num_partitions), &crc);
+      write_diagnostic_classification (to, global_dc, &crc);
+    }
   if (config.macro_locs)
     write_macro_maps (to, map_info, &crc);
 
@@ -20071,6 +20242,10 @@ module_state::read_initial (cpp_reader *reader)
   else if (!read_ordinary_maps (config.ordinary_locs, config.loc_range_bits))
     ok = false;
 
+  if (ok && have_locs && config.ordinary_locs
+      && !read_diagnostic_classification (global_dc))
+    ok = false;
+
   /* Allocate the REMAP vector.  */
   slurp->alloc_remap (config.num_imports);
 
diff --git a/gcc/testsuite/g++.dg/modules/warn-spec-3_a.C 
b/gcc/testsuite/g++.dg/modules/warn-spec-3_a.C
new file mode 100644
index 000000000000..2e50303c41e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/warn-spec-3_a.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++20 } }
+// { dg-additional-options -fmodules }
+// { dg-module-cmi M }
+
+module;
+
+#include <initializer_list>
+
+export module M;
+
+#pragma GCC diagnostic ignored "-Winit-list-lifetime"
+
+template <class T>
+struct myspan {
+  const T* p; unsigned s;
+  myspan (std::initializer_list<T> il)
+    : p (il.begin()), s (il.size()) { }
+};
+
+export void f(myspan<int>);
diff --git a/gcc/testsuite/g++.dg/modules/warn-spec-3_b.C 
b/gcc/testsuite/g++.dg/modules/warn-spec-3_b.C
new file mode 100644
index 000000000000..0a614f72bec6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/warn-spec-3_b.C
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fmodules -fdump-lang-module" }
+
+// Test that we don't re-export the changes from M.
+// { dg-final { scan-lang-dump {Diagnostic changes: 0} module } }
+
+export module N;
+import M;
diff --git a/gcc/testsuite/g++.dg/modules/warn-spec-3_c.C 
b/gcc/testsuite/g++.dg/modules/warn-spec-3_c.C
new file mode 100644
index 000000000000..2e466c09c8b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/warn-spec-3_c.C
@@ -0,0 +1,12 @@
+// { dg-additional-options "-fmodules -fdump-lang-module" }
+
+// Test that we clean up the unpopped change in M.
+// { dg-final { scan-lang-dump {Adding final pop} module } }
+
+import N;
+import M;
+
+int main()
+{
+  f({24,42});
+}
diff --git a/libcpp/line-map.cc b/libcpp/line-map.cc
index 284af5781dcf..33701b519e1f 100644
--- a/libcpp/line-map.cc
+++ b/libcpp/line-map.cc
@@ -767,6 +767,17 @@ linemap_module_restore (line_maps *set, line_map_uint_t 
lwm)
   return 0;
 }
 
+/* TRUE iff the location comes from a module import.  */
+
+bool
+linemap_location_from_module_p (const line_maps *set, location_t loc)
+{
+  const line_map_ordinary *map = linemap_ordinary_map_lookup (set, loc);
+  while (map && map->reason != LC_MODULE)
+    map = linemap_included_from_linemap (set, map);
+  return !!map;
+}
+
 /* Returns TRUE if the line table set tracks token locations across
    macro expansion, FALSE otherwise.  */

Reply via email to