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

Reply via email to