On Wed, 10 Dec 2025, Jason Merrill wrote:

> Actually applying this revised version.  Tested x86_64-pc-linux-gnu.
> 
> -- 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.

Looks like cpp_get_name is no longer defined in this version of the
patch.

>       (_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.
> 
> gcc/ChangeLog:
> 
>       * doc/invoke.texi (C++ Modules): Remove standard library header
>       units from missing pieces, mention importable header redirection.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/modules/compile-std1.C: Test <vector> translation.
> ---
>  gcc/doc/invoke.texi                         |  15 +-
>  libcpp/include/cpplib.h                     |   3 +-
>  gcc/cp/module.cc                            |  81 ++++++++-
>  gcc/testsuite/g++.dg/modules/compile-std1.C |   4 +-
>  libcpp/files.cc                             | 189 ++++++++++++--------
>  5 files changed, 204 insertions(+), 88 deletions(-)
> 
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 813403a9733..8db0aa0ceb7 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -38780,13 +38780,6 @@ reverse is not implemented---textually redefining an 
> entity that has
>  been defined in an imported header-unit.  A redefinition error is
>  emitted.
>  
> -@item Standard Library Header Units
> -The Standard Library is not provided as importable header units.  If
> -you want to import such units, you must explicitly build them first.
> -If you do not do this with care, you may have multiple declarations,
> -which the module machinery must merge---compiler resource usage can be
> -affected by how you partition header files into header units.
> -
>  @end table
>  
>  Modular compilation is @emph{not} enabled with just the
> @@ -38849,6 +38842,14 @@ and any standard library #includes in mycode.C will 
> be skipped,
>  because the import brought in the whole library.  This can be a simple
>  way to use modules to speed up compilation without any code changes.
>  
> +But for the standard library in particular this is unnecessary: if a
> +header unit has been built for the libstdc++ @samp{bits/stdc++.h}
> +header, the compiler will translate an @samp{#include} of any
> +importable standard library header into an import of that header unit,
> +speeding up compilation without needing to specify @samp{-include}.
> +Note that the @samp{bits/stdc++.h} header unit is also built by the
> +@option{--compile-std-module} option.
> +
>  The @option{-fmodule-only} option disables generation of the
>  associated object file for compiling a module interface.  Only the CMI
>  is generated.  This option is implied when using the
> diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
> index 16f030c82f3..65e1bc6aae0 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
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index 5c70e9bb469..7899fac8a2d 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -22541,11 +22541,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 ())
>      {
> @@ -22554,6 +22609,8 @@ maybe_translate_include (cpp_reader *reader, 
> line_maps *lmaps, location_t loc,
>        return nullptr;
>      }
>  
> +  const char *path = _cpp_get_file_path (file);
> +
>    dump.push (NULL);
>  
>    dump () && dump ("Checking include translation '%s'", path);
> @@ -22599,6 +22656,28 @@ 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_file_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 <version> which often goes with import std.  */
> +       && strcmp (fname, "version") != 0
> +       /* Don't redirect #includes between headers under the same include
> +          path directory (i.e. between library headers); if the import
> +          brings in the current file we then get redefinition errors.  */
> +       && !strstr (eloc.file, _cpp_get_file_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/gcc/testsuite/g++.dg/modules/compile-std1.C 
> b/gcc/testsuite/g++.dg/modules/compile-std1.C
> index a03a87569a8..1de9ea78976 100644
> --- a/gcc/testsuite/g++.dg/modules/compile-std1.C
> +++ b/gcc/testsuite/g++.dg/modules/compile-std1.C
> @@ -1,12 +1,14 @@
>  // { dg-additional-options "-fmodules --compile-std-module -g -O" }
> +// { dg-additional-options "-flang-info-include-translate" }
>  // { dg-do compile { target c++20 } }
>  // { dg-module-cmi std }
>  // { dg-module-cmi std.compat }
>  // { dg-module-cmi <bits/stdc++.h> }
>  
> -import <bits/stdc++.h>;
>  import std;
>  import std.compat;
> +#include <vector>            // { dg-message "translated to import" }
> +import <bits/stdc++.h>;
>  
>  void f()
>  {
> diff --git a/libcpp/files.cc b/libcpp/files.cc
> index f8b33129486..dd65848de7c 100644
> --- a/libcpp/files.cc
> +++ b/libcpp/files.cc
> @@ -211,6 +211,7 @@ static bool validate_pch (cpp_reader *, _cpp_file *file, 
> const char *pchname);
>  static int pchf_save_compare (const void *e1, const void *e2);
>  static int pchf_compare (const void *d_p, const void *e_p);
>  static bool check_file_against_entries (cpp_reader *, _cpp_file *, bool);
> +static void _cpp_post_stack_file (cpp_reader *, _cpp_file *, include_type, 
> bool);
>  
>  /* Given a filename in FILE->PATH, with the empty string interpreted
>     as <stdin>, open it.
> @@ -954,88 +955,92 @@ 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)
> +  /* 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;
> +}
> +
> +/* Like _cpp_stack_file, but for a file that's been replaced by the contents 
> of
> +   BUF.  Used for C++ modules include -> import translation.  */
> +
> +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)
>      {
> -      /* 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);
> -    }
> -  else
> -    {
> -      /* Not a header unit, and we know it.  */
> -      file->header_unit = -1;
> -
> -      if (!read_file (pfile, file, loc))
> -     return false;
> -
> -      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;
> +      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);

Maybe this is a preexisting issue made more obvious by the refactoring,
but shouldn't we pass sysp=true when translating a system header?

> +  return true;
> +}
> +
> +/* The common epilogue of _cpp_stack_file and _cpp_stack_translated_file.  */
> +
> +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 +1075,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 +1174,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.  */
> 
> base-commit: 23d71494fe0a6244b870db5a4f879b528903f52c
> -- 
> 2.52.0
> 
> 

Reply via email to