Tested x86_64-pc-linux-gnu.  Any comments?

-- 8< --

Since the standard library doesn't preclude an #include of a standard
library header from bringing in declarations from other headers, we can
translate an #include of any of the importable headers as an import of
<bits/stdc++.h>.

To reduce the amount of C++ standard knowledge encoded in libcpp, I extend
the translate_include callback to allow it to suggest an alternate header to
try translating.  It's a bit awkward to bounce back and forth, but this
seems like the right division of responsibilities.

libcpp/ChangeLog:

        * include/cpplib.h (struct cpp_callbacks): Replace 'path' parameter
        with file, angle_brackets, and alternate name.
        (cpp_get_name): Declare.
        * files.cc (cpp_get_name): New.
        (_cpp_stack_include, _cpp_post_stack_file, _cpp_stack_file)
        (_cpp_stack_translated_file): Refactor, try alternate file.

gcc/cp/ChangeLog:

        * module.cc (maybe_translate_include): Suggest <bits/stdc++.h>
        as an alternate for importable standard library headers.
        (importable_headers, is_importable_header): New.
---
 libcpp/include/cpplib.h |   4 +-
 gcc/cp/module.cc        |  80 ++++++++++++++++-
 libcpp/files.cc         | 189 ++++++++++++++++++++++++----------------
 3 files changed, 195 insertions(+), 78 deletions(-)

diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 5190ff7d08f..d38889b893e 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -860,7 +860,8 @@ struct cpp_callbacks
   /* Maybe translate a #include into something else.  Return a
      cpp_buffer containing the translation if translating.  */
   char *(*translate_include) (cpp_reader *, line_maps *, location_t,
-                             const char *path);
+                             _cpp_file *file, bool angle_brackets,
+                             const char **alternate);
 };
 
 #ifdef VMS
@@ -1564,6 +1565,7 @@ extern bool cpp_push_include (cpp_reader *, const char *);
 extern bool cpp_push_default_include (cpp_reader *, const char *);
 extern void cpp_change_file (cpp_reader *, enum lc_reason, const char *);
 extern const char *cpp_get_path (struct _cpp_file *);
+extern const char *cpp_get_name (struct _cpp_file *);
 extern cpp_dir *cpp_get_dir (struct _cpp_file *);
 extern cpp_buffer *cpp_get_buffer (cpp_reader *);
 extern struct _cpp_file *cpp_get_file (cpp_buffer *);
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 45a0309b86e..a9c40212434 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -22488,11 +22488,66 @@ void module_state::set_filename (const Cody::Packet 
&packet)
     }
 }
 
+/* The list of importable headers from C++ Table 24.  */
+
+static const char *
+importable_headers[] =
+  {
+    "algorithm", "any", "array", "atomic",
+    "barrier", "bit", "bitset",
+    "charconv", "chrono", "compare", "complex", "concepts",
+    "condition_variable", "contracts", "coroutine",
+    "debugging", "deque",
+    "exception", "execution", "expected",
+    "filesystem", "flat_map", "flat_set", "format", "forward_list",
+    "fstream", "functional", "future",
+    "generator",
+    "hazard_pointer", "hive",
+    "initializer_list", "inplace_vector", "iomanip", "ios", "iosfwd",
+    "iostream", "istream", "iterator",
+    "latch", "limits", "linalg", "list", "locale",
+    "map", "mdspan", "memory", "memory_resource", "meta", "mutex",
+    "new", "numbers", "numeric",
+    "optional", "ostream",
+    "print",
+    "queue",
+    "random", "ranges", "ratio", "rcu", "regex",
+    "scoped_allocator", "semaphore", "set", "shared_mutex", "simd",
+    "source_location", "span", "spanstream", "sstream", "stack", "stacktrace",
+    "stdexcept", "stdfloat", "stop_token", "streambuf", "string",
+    "string_view", "syncstream", "system_error",
+    "text_encoding", "thread", "tuple", "type_traits", "typeindex", "typeinfo",
+    "unordered_map", "unordered_set",
+    "utility",
+    "valarray", "variant", "vector", "version"
+  };
+
+/* True iff <name> is listed as an importable standard header.  */
+
+static bool
+is_importable_header (const char *name)
+{
+  unsigned lo = 0;
+  unsigned hi = ARRAY_SIZE (importable_headers);
+  while (hi > lo)
+    {
+      unsigned mid = (lo + hi)/2;
+      int cmp = strcmp (name, importable_headers[mid]);
+      if (cmp > 0)
+       lo = mid + 1;
+      else if (cmp < 0)
+       hi = mid;
+      else
+       return true;
+    }
+  return false;
+}
+
 /* Figure out whether to treat HEADER as an include or an import.  */
 
 static char *
 maybe_translate_include (cpp_reader *reader, line_maps *lmaps, location_t loc,
-                        const char *path)
+                        _cpp_file *file, bool angle, const char **alternate)
 {
   if (!modules_p ())
     {
@@ -22501,6 +22556,8 @@ maybe_translate_include (cpp_reader *reader, line_maps 
*lmaps, location_t loc,
       return nullptr;
     }
 
+  const char *path = cpp_get_path (file);
+
   dump.push (NULL);
 
   dump () && dump ("Checking include translation '%s'", path);
@@ -22546,6 +22603,27 @@ maybe_translate_include (cpp_reader *reader, line_maps 
*lmaps, location_t loc,
       if (!strcmp ((*note_includes)[ix], path))
        note = true;
 
+  /* Maybe try importing a different header instead.  */
+  if (alternate && translate == xlate_kind::unknown)
+    {
+      const char *fname = cpp_get_name (file);
+      /* Redirect importable <name> to <bits/stdc++.h>.  */
+      /* ??? Generalize to use a .json.  */
+      expanded_location eloc = expand_location (loc);
+      if (angle && is_importable_header (fname)
+         /* Exclude #include <version> which often goes with import std.  */
+         && strcmp (fname, "version") != 0
+         /* Don't redirect #includes between library headers; if the import
+            brings in the current file we then get redefinition errors.  */
+         && !strstr (eloc.file, cpp_get_dir (file)->name)
+         /* ??? These are needed when running a toolchain from the build
+            directory, because libsupc++ headers aren't linked into
+            libstdc++-v3/include with the other headers.  */
+         && !strstr (eloc.file, "libstdc++-v3/include")
+         && !strstr (eloc.file, "libsupc++"))
+       *alternate = "bits/stdc++.h";
+    }
+
   if (note)
     inform (loc, translate == xlate_kind::import
            ? G_("include %qs translated to import")
diff --git a/libcpp/files.cc b/libcpp/files.cc
index d80c4bfd907..a28f1622ced 100644
--- a/libcpp/files.cc
+++ b/libcpp/files.cc
@@ -946,6 +946,42 @@ has_unique_contents (cpp_reader *pfile, _cpp_file *file, 
bool import,
   return true;
 }
 
+static void _cpp_post_stack_file (cpp_reader *, _cpp_file *, include_type, 
bool);
+
+static bool
+_cpp_stack_translated_file (cpp_reader *pfile, _cpp_file *file,
+                           char *buf, include_type type)
+{
+  /* We don't increment the line number at the end of a buffer,
+     because we don't usually need that location (we're popping an
+     include file).  However in this case we do want to do the
+     increment.  So push a writable buffer of two newlines to acheive
+     that.  (We also need an extra newline, so this looks like a regular
+     file, which we do that to to make sure we don't fall off the end in the
+     middle of a line.  */
+  if (type != IT_CMDLINE)
+    {
+      static uchar newlines[] = "\n\n\n";
+      cpp_push_buffer (pfile, newlines, 2, true);
+    }
+
+  size_t len = strlen (buf);
+  buf[len] = '\n'; /* See above  */
+  cpp_buffer *buffer
+    = cpp_push_buffer (pfile, reinterpret_cast<unsigned char *> (buf),
+                      len, true);
+  buffer->to_free = buffer->buf;
+  if (type == IT_CMDLINE)
+    /* Tell _cpp_pop_buffer to change files.  */
+    buffer->file = file;
+
+  file->header_unit = +1;
+  _cpp_mark_file_once_only (pfile, file);
+
+  _cpp_post_stack_file (pfile, file, type, false);
+  return true;
+}
+
 /* Place the file referenced by FILE into a new buffer on the buffer
    stack if possible.  Returns true if a buffer is stacked.  Use LOC
    for any diagnostics.  */
@@ -954,88 +990,53 @@ bool
 _cpp_stack_file (cpp_reader *pfile, _cpp_file *file, include_type type,
                 location_t loc)
 {
-  if (is_known_idempotent_file (pfile, file, type == IT_IMPORT))
+  int sysp = 0;
+
+  /* Not a header unit, and we know it.  */
+  file->header_unit = -1;
+
+  if (!read_file (pfile, file, loc))
     return false;
 
-  int sysp = 0;
-  char *buf = nullptr;
+  if (!has_unique_contents (pfile, file, type == IT_IMPORT, loc))
+    return false;
 
-  /* Check C++ module include translation.  */
-  if (!file->header_unit && type < IT_HEADER_HWM
-      /* Do not include translate include-next.  */
-      && type != IT_INCLUDE_NEXT
-      && pfile->cb.translate_include)
-    buf = (pfile->cb.translate_include
-          (pfile, pfile->line_table, loc, file->path));
+  if (pfile->buffer && file->dir)
+    sysp = MAX (pfile->buffer->sysp, file->dir->sysp);
 
-  if (buf)
-    {
-      /* We don't increment the line number at the end of a buffer,
-        because we don't usually need that location (we're popping an
-        include file).  However in this case we do want to do the
-        increment.  So push a writable buffer of two newlines to acheive
-        that.  (We also need an extra newline, so this looks like a regular
-        file, which we do that to to make sure we don't fall off the end in the
-        middle of a line.  */
-      if (type != IT_CMDLINE)
-       {
-         static uchar newlines[] = "\n\n\n";
-         cpp_push_buffer (pfile, newlines, 2, true);
-       }
+  /* Add the file to the dependencies on its first inclusion.  */
+  if (CPP_OPTION (pfile, deps.style) > (sysp != 0)
+      && !file->stack_count
+      && file->path[0]
+      && !(pfile->main_file == file
+          && CPP_OPTION (pfile, deps.ignore_main_file)))
+    deps_add_dep (pfile->deps, file->path);
 
-      size_t len = strlen (buf);
-      buf[len] = '\n'; /* See above  */
-      cpp_buffer *buffer
-       = cpp_push_buffer (pfile, reinterpret_cast<unsigned char *> (buf),
-                          len, true);
-      buffer->to_free = buffer->buf;
-      if (type == IT_CMDLINE)
-       /* Tell _cpp_pop_buffer to change files.  */
-       buffer->file = file;
+  /* Clear buffer_valid since _cpp_clean_line messes it up.  */
+  file->buffer_valid = false;
+  file->stack_count++;
 
-      file->header_unit = +1;
-      _cpp_mark_file_once_only (pfile, file);
-    }
-  else
-    {
-      /* Not a header unit, and we know it.  */
-      file->header_unit = -1;
+  /* Stack the buffer.  */
+  cpp_buffer *buffer
+    = cpp_push_buffer (pfile, file->buffer, file->st.st_size,
+                      CPP_OPTION (pfile, preprocessed)
+                      && !CPP_OPTION (pfile, directives_only));
+  buffer->file = file;
+  buffer->sysp = sysp;
+  buffer->to_free = file->buffer_start;
 
-      if (!read_file (pfile, file, loc))
-       return false;
+  /* Initialize controlling macro state.  */
+  pfile->mi_valid = true;
+  pfile->mi_cmacro = 0;
 
-      if (!has_unique_contents (pfile, file, type == IT_IMPORT, loc))
-       return false;
-
-      if (pfile->buffer && file->dir)
-       sysp = MAX (pfile->buffer->sysp, file->dir->sysp);
-
-      /* Add the file to the dependencies on its first inclusion.  */
-      if (CPP_OPTION (pfile, deps.style) > (sysp != 0)
-         && !file->stack_count
-         && file->path[0]
-         && !(pfile->main_file == file
-              && CPP_OPTION (pfile, deps.ignore_main_file)))
-       deps_add_dep (pfile->deps, file->path);
-
-      /* Clear buffer_valid since _cpp_clean_line messes it up.  */
-      file->buffer_valid = false;
-      file->stack_count++;
-
-      /* Stack the buffer.  */
-      cpp_buffer *buffer
-       = cpp_push_buffer (pfile, file->buffer, file->st.st_size,
-                          CPP_OPTION (pfile, preprocessed)
-                          && !CPP_OPTION (pfile, directives_only));
-      buffer->file = file;
-      buffer->sysp = sysp;
-      buffer->to_free = file->buffer_start;
-
-      /* Initialize controlling macro state.  */
-      pfile->mi_valid = true;
-      pfile->mi_cmacro = 0;
-    }
+  _cpp_post_stack_file (pfile, file, type, sysp);
+  return true;
+}
 
+static void
+_cpp_post_stack_file (cpp_reader *pfile, _cpp_file *file, include_type type,
+                     bool sysp)
+{
   /* In the case of a normal #include, we're now at the start of the
      line *following* the #include.  A separate location_t for this
      location makes no sense, until we do the LC_LEAVE.
@@ -1070,8 +1071,6 @@ _cpp_stack_file (cpp_reader *pfile, _cpp_file *file, 
include_type type,
       linenum_type line = SOURCE_LINE (map, pfile->line_table->highest_line);
       linemap_line_start (pfile->line_table, line - 1, 0);
     }
-
-  return true;
 }
 
 /* Mark FILE to be included once only.  */
@@ -1171,7 +1170,37 @@ _cpp_stack_include (cpp_reader *pfile, const char 
*fname, int angle_brackets,
   if (type == IT_DEFAULT && file == NULL)
     return false;
 
-  return _cpp_stack_file (pfile, file, type, loc);
+  if (is_known_idempotent_file (pfile, file, type == IT_IMPORT))
+    return false;
+
+  /* Check C++ module include translation.  */
+  char *buf = nullptr;
+  if (!file->header_unit && type < IT_DEFAULT
+      /* Do not include translate include-next.  */
+      && type != IT_INCLUDE_NEXT
+      && pfile->cb.translate_include)
+    {
+      const char *aname = nullptr;
+      buf = (pfile->cb.translate_include
+            (pfile, pfile->line_table, loc, file,
+             angle_brackets, &aname));
+      if (!buf && aname)
+       {
+         _cpp_file *afile = _cpp_find_file (pfile, aname, dir, angle_brackets,
+                                            _cpp_FFK_NORMAL, loc);
+         if (afile && !afile->header_unit)
+           buf = (pfile->cb.translate_include
+                  (pfile, pfile->line_table, loc,
+                   afile, angle_brackets, nullptr));
+         if (buf)
+           file = afile;
+       }
+    }
+
+  if (buf)
+    return _cpp_stack_translated_file (pfile, file, buf, type);
+  else
+    return _cpp_stack_file (pfile, file, type, loc);
 }
 
 /* NAME is a header file name, find the _cpp_file, if any.  */
@@ -2584,6 +2613,14 @@ cpp_get_path (struct _cpp_file *f)
   return f->path;
 }
 
+/* Get the base name specified in the directive.  */
+
+const char *
+cpp_get_name (struct _cpp_file *f)
+{
+  return f->name;
+}
+
 /* Get the directory associated with the _cpp_file F.  */
 
 cpp_dir *

base-commit: aaa625a51cff750b40bc98f6555adc3f3f5f297b
-- 
2.51.0

Reply via email to