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