Does this seem like an interesting direction?
-- 8< --
A problem with coexistence of module std and the library headers is that
import and then #include tends to break (PR99000). But even with that
fixed, it might be useful to be able to test whether a module has been
imported. So, this patch implements __has_import, along the same lines as
__has_builtin and such.
This does not test whether an import is available, which seems too variable
of a property; rather, it tests whether we've already seen an import in this
TU.
And then I change a few libstdc++ files to demonstrate how I imagine it
being used.
A shortcoming of this approach is that we probably aren't going to change
the C library headers, and a significant case where we would want to both
import and #include is to get macros from those headers. So we still need
to fix PR99000 at least enough for those headers. But that seems likely to
be a smaller task than fixing it for everything in the C++ headers.
libcpp/ChangeLog:
* init.cc (builtin_array): Add __has_import.
* include/cpplib.h (enum cpp_builtin_type): Add BT_HAS_IMPORT.
(struct cpp_hashnode_extra): Add imported.
* internal.h (_cpp_module_track_import): Declare.
* lex.cc (_cpp_module_track_import): New.
(cpp_maybe_module_directive): Call it.
* macro.cc (builtin_has_import): Likewise.
(_cpp_builtin_macro_text): Call builtin_has_import.
libstdc++-v3/ChangeLog:
* include/bits/c++config: Define _GLIBCXX_MACROS_ONLY if
__has_import(std).
* include/bits/postypes.h: Check _GLIBCXX_MACROS_ONLY.
* include/std/iosfwd: Likewise.
* include/std/iostream: Likewise.
* include/std/istream: Likewise.
* include/std/ostream: Likewise.
* libsupc++/exception: Likewise.
---
libcpp/include/cpplib.h | 2 +
libcpp/internal.h | 1 +
libstdc++-v3/include/bits/postypes.h | 2 +
libcpp/init.cc | 1 +
libcpp/lex.cc | 85 ++++++++++++++++++++++++++++
libcpp/macro.cc | 32 +++++++++++
libstdc++-v3/include/bits/c++config | 11 ++++
libstdc++-v3/include/std/iosfwd | 4 ++
libstdc++-v3/include/std/iostream | 2 +
libstdc++-v3/include/std/istream | 2 +
libstdc++-v3/include/std/ostream | 3 +
libstdc++-v3/libsupc++/exception | 9 ++-
12 files changed, 152 insertions(+), 2 deletions(-)
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index e73f77e67d8..f8b5c5d90d1 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -1021,6 +1021,7 @@ enum cpp_builtin_type
BT_HAS_ATTRIBUTE, /* `__has_attribute(x)' */
BT_HAS_STD_ATTRIBUTE, /* `__has_c_attribute(x)' */
BT_HAS_BUILTIN, /* `__has_builtin(x)' */
+ BT_HAS_IMPORT, /* `__has_import(x)' */
BT_HAS_INCLUDE, /* `__has_include(x)' */
BT_HAS_INCLUDE_NEXT, /* `__has_include_next(x)' */
BT_HAS_EMBED, /* `__has_embed(x)' */
@@ -1075,6 +1076,7 @@ struct GTY(()) cpp_hashnode_extra
{
struct ht_identifier ident;
location_t poisoned_loc;
+ bool imported;
};
/* A class for iterating through the source locations within a
diff --git a/libcpp/internal.h b/libcpp/internal.h
index e65198e89da..9fb56f48867 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -813,6 +813,7 @@ static inline void *_cpp_reserve_room (cpp_reader *pfile,
size_t have,
return BUFF_FRONT (pfile->a_buff);
}
extern void *_cpp_commit_buff (cpp_reader *pfile, size_t size);
+extern bool _cpp_module_track_import (cpp_reader *, const cpp_token *, bool);
/* In init.cc. */
extern void _cpp_maybe_push_include_file (cpp_reader *);
diff --git a/libstdc++-v3/include/bits/postypes.h
b/libstdc++-v3/include/bits/postypes.h
index 8a2f4dbc937..36eba5a95a0 100644
--- a/libstdc++-v3/include/bits/postypes.h
+++ b/libstdc++-v3/include/bits/postypes.h
@@ -41,6 +41,7 @@
#include <cwchar> // For mbstate_t
+#ifndef _GLIBCXX_MACROS_ONLY
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -224,5 +225,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
+#endif /* _GLIBCXX_MACROS_ONLY */
#endif
diff --git a/libcpp/init.cc b/libcpp/init.cc
index 355e5017649..1df2e63400e 100644
--- a/libcpp/init.cc
+++ b/libcpp/init.cc
@@ -461,6 +461,7 @@ static const struct builtin_macro builtin_array[] =
B("__has_c_attribute", BT_HAS_STD_ATTRIBUTE, true),
B("__has_cpp_attribute", BT_HAS_ATTRIBUTE, true),
B("__has_builtin", BT_HAS_BUILTIN, true),
+ B("__has_import", BT_HAS_IMPORT, true),
B("__has_include", BT_HAS_INCLUDE, true),
B("__has_include_next",BT_HAS_INCLUDE_NEXT, true),
B("__has_embed", BT_HAS_EMBED, true),
diff --git a/libcpp/lex.cc b/libcpp/lex.cc
index 849447eb4d7..59943290ca6 100644
--- a/libcpp/lex.cc
+++ b/libcpp/lex.cc
@@ -3489,6 +3489,88 @@ _cpp_temp_token (cpp_reader *pfile)
return result;
}
+/* Parse a module name consisting of identifiers separated by dots, where FIRST
+ is the first identifier. If SETTING, record it, rewind the tokens, and
+ return true on success; otherwise return whether we've seen it. */
+
+bool
+_cpp_module_track_import (cpp_reader *pfile, const cpp_token *first,
+ bool setting)
+{
+ cpp_token *next = _cpp_lex_direct (pfile);
+ int backup = 1;
+ bool ok = true;
+
+ cpp_hashnode_extra *data = nullptr;
+ const ht_lookup_option lopt = setting ? HT_ALLOC : HT_NO_INSERT;
+
+ if (next->type != CPP_DOT)
+ /* Easy case, just look up the single id. */
+ data = (cpp_hashnode_extra *)
+ ht_lookup (pfile->extra_hash_table,
+ first->val.node.node->ident, lopt);
+ else
+ {
+ /* We need to paste all the tokens together. */
+ unsigned cap = 256;
+ uchar *buf = XNEWVEC (uchar, cap);
+ uchar *end = buf;
+
+ auto append = [&](const cpp_token *tok)
+ {
+ unsigned tlen = cpp_token_len (tok);
+ if ((end - buf) + tlen + 1 > cap)
+ {
+ cap = (cap + tlen) * 2;
+ buf = XRESIZEVEC (uchar, buf, cap);
+ }
+ end = cpp_spell_token (pfile, tok, end, true);
+ };
+
+ append (first);
+ while (next->type == CPP_DOT)
+ {
+ append (next);
+ next = _cpp_lex_direct (pfile);
+ ++backup;
+ if (next->type != CPP_NAME)
+ {
+ cpp_error (pfile, CPP_DL_ERROR,
+ "expected identifier in module name");
+ ok = false;
+ break;
+ }
+ append (next);
+ next = _cpp_lex_direct (pfile);
+ ++backup;
+ }
+
+ if (ok)
+ data = (cpp_hashnode_extra *)
+ ht_lookup (pfile->extra_hash_table, buf, end-buf, lopt);
+
+ XDELETEVEC (buf);
+ }
+
+ /* When observing an import, we don't want to consume the tokens, so backup
+ all of them. When parsing __has_import, we want to consume the name
+ tokens, but still need to backup the one token after the name. */
+ if (setting)
+ _cpp_backup_tokens_direct (pfile, backup);
+ else
+ _cpp_backup_tokens_direct (pfile, 1);
+
+ if (!data || !ok)
+ return false;
+ else if (setting)
+ {
+ data->imported = true;
+ return true;
+ }
+ else
+ return data->imported;
+}
+
/* We're at the beginning of a logical line (so not in
directives-mode) and RESULT is a CPP_NAME with NODE_MODULE set. See
if we should enter deferred_pragma mode to tokenize the rest of the
@@ -3605,6 +3687,9 @@ cpp_maybe_module_directive (cpp_reader *pfile, cpp_token
*result)
road. */
pfile->state.directive_file_token = header_count;
+ if (header_count && peek->type == CPP_NAME)
+ _cpp_module_track_import (pfile, peek, /*set*/true);
+
/* According to P3034R1, pp-module-name and pp-module-partition tokens
if any shouldn't be macro expanded and identifiers shouldn't be
defined as object-like macro. */
diff --git a/libcpp/macro.cc b/libcpp/macro.cc
index 907af873df1..b252253cac7 100644
--- a/libcpp/macro.cc
+++ b/libcpp/macro.cc
@@ -515,6 +515,34 @@ builtin_has_embed (cpp_reader *pfile)
return result;
}
+/* Handle "__has_import(module.name)". */
+
+static int
+builtin_has_import (cpp_reader *pfile)
+{
+ int result = 0;
+ const cpp_token *token = _cpp_get_token_no_padding (pfile);
+ bool paren = token->type == CPP_OPEN_PAREN;
+ if (paren)
+ token = _cpp_get_token_no_padding (pfile);
+ else
+ cpp_error (pfile, CPP_DL_ERROR,
+ "missing %<(%> before %qs operand", "__has_import");
+
+ if (token->type != CPP_NAME)
+ cpp_error (pfile, CPP_DL_ERROR,
+ "%qs requires a module name", "__has_import");
+ else
+ result = _cpp_module_track_import (pfile, token, /*set*/false);
+
+ if (paren
+ && _cpp_get_token_no_padding (pfile)->type != CPP_CLOSE_PAREN)
+ cpp_error (pfile, CPP_DL_ERROR,
+ "missing %<)%> after %qs operand", "__has_import");
+
+ return result;
+}
+
/* Emits a warning if NODE is a macro defined in the main file that
has not been used. */
int
@@ -747,6 +775,10 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode
*node,
number = pfile->cb.has_builtin (pfile);
break;
+ case BT_HAS_IMPORT:
+ number = builtin_has_import (pfile);
+ break;
+
case BT_HAS_INCLUDE:
case BT_HAS_INCLUDE_NEXT:
number = builtin_has_include (pfile, node,
diff --git a/libstdc++-v3/include/bits/c++config
b/libstdc++-v3/include/bits/c++config
index 236906d2f79..97afc82298c 100644
--- a/libstdc++-v3/include/bits/c++config
+++ b/libstdc++-v3/include/bits/c++config
@@ -48,6 +48,13 @@
#undef __GLIBCXX__ /* The testsuite defines it to 99999999 to block PCH. */
#define __GLIBCXX__
+// If we've already imported std, only define macros.
+#ifdef __has_import
+#if __has_import (std) || __has_import (std.compat)
+#define _GLIBCXX_MACROS_ONLY
+#endif
+#endif
+
// Macros for various attributes.
// _GLIBCXX_PURE
// _GLIBCXX_CONST
@@ -325,6 +332,7 @@
For full details see:
http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/namespaces.html
*/
+#ifndef _GLIBCXX_MACROS_ONLY
namespace std
{
typedef __SIZE_TYPE__ size_t;
@@ -345,6 +353,7 @@ namespace std
}
#pragma GCC visibility pop
}
+#endif
#define _GLIBCXX_USE_DUAL_ABI
@@ -562,6 +571,7 @@ namespace std
#endif // _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT && IEEE128
+#ifndef _GLIBCXX_MACROS_ONLY
namespace std
{
#pragma GCC visibility push(default)
@@ -585,6 +595,7 @@ namespace std
}
#pragma GCC visibility pop
}
+#endif
#ifndef _GLIBCXX_ASSERTIONS
# if defined(_GLIBCXX_DEBUG)
diff --git a/libstdc++-v3/include/std/iosfwd b/libstdc++-v3/include/std/iosfwd
index 9051b226d5c..d1b8a8e77cf 100644
--- a/libstdc++-v3/include/std/iosfwd
+++ b/libstdc++-v3/include/std/iosfwd
@@ -40,6 +40,9 @@
#include <bits/requires_hosted.h> // iostreams
#include <bits/c++config.h>
+
+#ifndef _GLIBCXX_MACROS_ONLY
+
#include <bits/stringfwd.h> // For string forward declarations.
#include <bits/postypes.h>
@@ -257,4 +260,5 @@ _GLIBCXX_END_NAMESPACE_CXX11
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
+#endif /* _GLIBCXX_MACROS_ONLY */
#endif /* _GLIBCXX_IOSFWD */
diff --git a/libstdc++-v3/include/std/iostream
b/libstdc++-v3/include/std/iostream
index 25064b322ad..532f5f64adf 100644
--- a/libstdc++-v3/include/std/iostream
+++ b/libstdc++-v3/include/std/iostream
@@ -43,6 +43,7 @@
#include <ostream>
#include <istream>
+#ifndef _GLIBCXX_MACROS_ONLY
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -86,5 +87,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
+#endif /* _GLIBCXX_MACROS_ONLY */
#endif /* _GLIBCXX_IOSTREAM */
diff --git a/libstdc++-v3/include/std/istream b/libstdc++-v3/include/std/istream
index ef773977ed4..8fdaeae2666 100644
--- a/libstdc++-v3/include/std/istream
+++ b/libstdc++-v3/include/std/istream
@@ -42,6 +42,7 @@
#include <ios>
#include <ostream>
+#ifndef _GLIBCXX_MACROS_ONLY
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -1110,4 +1111,5 @@ _GLIBCXX_END_NAMESPACE_VERSION
#include <bits/istream.tcc>
+#endif /* _GLIBCXX_MACROS_ONLY */
#endif /* _GLIBCXX_ISTREAM */
diff --git a/libstdc++-v3/include/std/ostream b/libstdc++-v3/include/std/ostream
index 637aad5a5a4..4a9e0ab61d9 100644
--- a/libstdc++-v3/include/std/ostream
+++ b/libstdc++-v3/include/std/ostream
@@ -48,6 +48,7 @@
# define __glibcxx_want_print
#include <bits/version.h> // __glibcxx_syncbuf
+#ifndef _GLIBCXX_MACROS_ONLY
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -1052,4 +1053,6 @@ _GLIBCXX_END_NAMESPACE_VERSION
#include <bits/ostream.tcc>
+#endif /* _GLIBCXX_MACROS_ONLY */
+
#endif /* _GLIBCXX_OSTREAM */
diff --git a/libstdc++-v3/libsupc++/exception b/libstdc++-v3/libsupc++/exception
index 7ed8ce317d4..d905bd46605 100644
--- a/libstdc++-v3/libsupc++/exception
+++ b/libstdc++-v3/libsupc++/exception
@@ -35,11 +35,14 @@
#endif
#include <bits/c++config.h>
-#include <bits/exception.h>
-
#define __glibcxx_want_uncaught_exceptions
#include <bits/version.h>
+#ifndef _GLIBCXX_MACROS_ONLY
+
+#include <bits/exception.h>
+
+
extern "C++" {
namespace std _GLIBCXX_VISIBILITY(default)
@@ -169,4 +172,6 @@ _GLIBCXX_END_NAMESPACE_VERSION
#include <bits/nested_exception.h>
#endif
+#endif /* _GLIBCXX_MACROS_ONLY */
+
#endif
base-commit: e5050819808507cb8efb8ee36e835c6e1891bee9
--
2.47.0