mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 20:01:31 -05:00
c++/modules: #include <vector> -> import <bits/stdc++.h>
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. 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.
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
189
libcpp/files.cc
189
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);
|
||||
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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user