preprocessor: Add deferred macros
authorNathan Sidwell <nathan@acm.org>
Tue, 24 Nov 2020 16:23:55 +0000 (08:23 -0800)
committerNathan Sidwell <nathan@acm.org>
Tue, 24 Nov 2020 16:31:03 +0000 (08:31 -0800)
Deferred macros are needed for C++ modules.  Header units may export
macro definitions and undefinitions.  These are resolved lazily at the
point of (potential) use.  (The language specifies that, it's not just
a useful optimization.)  Thus, identifier nodes grow a 'deferred'
field, which fortunately doesn't expand the structure on 64-bit
systems as there was padding there.  This is non-zero on NT_MACRO
nodes, if the macro is deferred.  When such an identifier is lexed, it
is resolved via a callback that I added recently.  That will either
provide the macro definition, or discover it there was an overriding
undef.  Either way the identifier is no longer a deferred macro.
Notice it is now possible for NT_MACRO nodes to have a NULL macro
expansion.

libcpp/
* include/cpplib.h (struct cpp_hashnode): Add deferred field.
(cpp_set_deferred_macro): Define.
(cpp_get_deferred_macro): Declare.
(cpp_macro_definition): Reformat, add overload.
(cpp_macro_definition_location): Deal with deferred macro.
(cpp_alloc_token_string, cpp_compare_macro): Declare.
* internal.h (_cpp_notify_macro_use): Return bool
(_cpp_maybe_notify_macro_use): Likewise.
* directives.c (do_undef): Check macro is not undef before
warning.
(do_ifdef, do_ifndef): Deal with deferred macro.
* expr.c (parse_defined): Likewise.
* lex.c (cpp_allocate_token_string): Break out of ...
(create_literal): ... here.  Call it.
(cpp_maybe_module_directive): Deal with deferred macro.
* macro.c (cpp_get_token_1): Deal with deferred macro.
(warn_of_redefinition): Deal with deferred macro.
(compare_macros): Rename to ...
(cpp_compare_macro): ... here.  Make extern.
(cpp_get_deferred_macro): New.
(_cpp_notify_macro_use): Deal with deferred macro, return bool
indicating definedness.
(cpp_macro_definition): Deal with deferred macro.

libcpp/directives.c
libcpp/expr.c
libcpp/include/cpplib.h
libcpp/internal.h
libcpp/lex.c
libcpp/macro.c

index bffdc913adb92e0dc50d22a2691c511145677690..fa66b5c5f71260882aab66836fafd090f3d7fdb9 100644 (file)
@@ -667,7 +667,8 @@ do_undef (cpp_reader *pfile)
                                   pfile->directive_line, 0,
                                   "undefining \"%s\"", NODE_NAME (node));
 
-         if (CPP_OPTION (pfile, warn_unused_macros))
+         if (node->value.macro
+             && CPP_OPTION (pfile, warn_unused_macros))
            _cpp_warn_if_unused_macro (pfile, node, NULL);
 
          _cpp_free_definition (node);
@@ -1981,8 +1982,10 @@ do_ifdef (cpp_reader *pfile)
       if (node)
        {
          skip = !_cpp_defined_macro_p (node);
+         if (!_cpp_maybe_notify_macro_use (pfile, node, pfile->directive_line))
+           /* It wasn't a macro after all.  */
+           skip = true;
          _cpp_mark_macro_used (node);
-         _cpp_maybe_notify_macro_use (pfile, node, pfile->directive_line);
          if (pfile->cb.used)
            pfile->cb.used (pfile, pfile->directive_line, node);
          check_eol (pfile, false);
@@ -2006,8 +2009,10 @@ do_ifndef (cpp_reader *pfile)
       if (node)
        {
          skip = _cpp_defined_macro_p (node);
+         if (!_cpp_maybe_notify_macro_use (pfile, node, pfile->directive_line))
+           /* It wasn't a macro after all.  */
+           skip = false;
          _cpp_mark_macro_used (node);
-         _cpp_maybe_notify_macro_use (pfile, node, pfile->directive_line);
          if (pfile->cb.used)
            pfile->cb.used (pfile, pfile->directive_line, node);
          check_eol (pfile, false);
index b98c0386eb5d797b6627e4f705fed55f3ce343e1..2ba7726d61c4679edca4270eb5d1d4e02cf566be 100644 (file)
@@ -1068,6 +1068,7 @@ parse_defined (cpp_reader *pfile)
        }
     }
 
+  bool is_defined = false;
   if (node)
     {
       if ((pfile->context != initial_context
@@ -1075,9 +1076,11 @@ parse_defined (cpp_reader *pfile)
          && CPP_OPTION (pfile, warn_expansion_to_defined))
         cpp_pedwarning (pfile, CPP_W_EXPANSION_TO_DEFINED,
                        "this use of \"defined\" may not be portable");
-
+      is_defined = _cpp_defined_macro_p (node);
+      if (!_cpp_maybe_notify_macro_use (pfile, node, token->src_loc))
+       /* It wasn't a macro after all.  */
+       is_defined = false;
       _cpp_mark_macro_used (node);
-      _cpp_maybe_notify_macro_use (pfile, node, token->src_loc);
 
       /* A possible controlling macro of the form #if !defined ().
         _cpp_parse_expr checks there was no other junk on the line.  */
@@ -1093,7 +1096,7 @@ parse_defined (cpp_reader *pfile)
   result.unsignedp = false;
   result.high = 0;
   result.overflow = false;
-  result.low = node && _cpp_defined_macro_p (node);
+  result.low = is_defined;
   return result;
 }
 
index 91226cfc248d71b3fe77f97c96e754bd3aec6311..2becd2e8e545e76525f38ffbf7afea48117ad251 100644 (file)
@@ -901,7 +901,7 @@ enum cpp_builtin_type
 union GTY(()) _cpp_hashnode_value {
   /* Assert (maybe NULL) */
   cpp_macro * GTY((tag ("NT_VOID"))) answers;
-  /* Macro (never NULL) */
+  /* Macro (maybe NULL) */
   cpp_macro * GTY((tag ("NT_USER_MACRO"))) macro;
   /* Code for a builtin macro.  */
   enum cpp_builtin_type GTY ((tag ("NT_BUILTIN_MACRO"))) builtin;
@@ -919,7 +919,11 @@ struct GTY(()) cpp_hashnode {
   unsigned int flags : 9;              /* CPP flags.  */
   ENUM_BITFIELD(node_type) type : 2;   /* CPP node type.  */
 
-  /* 5 bits spare (plus another 32 on 64-bit hosts).  */
+  /* 5 bits spare.  */
+
+  /* On a 64-bit system there would be 32-bits of padding to the value
+     field.  So placing the deferred index here is not costly.   */
+  unsigned deferred;                   /* Deferred index, (unless zero).  */
 
   union _cpp_hashnode_value GTY ((desc ("%1.type"))) value;
 };
@@ -1061,6 +1065,18 @@ inline bool cpp_macro_p (const cpp_hashnode *node)
 {
   return node->type & NT_MACRO_MASK;
 }
+inline cpp_macro *cpp_set_deferred_macro (cpp_hashnode *node,
+                                         cpp_macro *forced = NULL)
+{
+  cpp_macro *old = node->value.macro;
+
+  node->value.macro = forced;
+  node->type = NT_USER_MACRO;
+  node->flags &= ~NODE_USED;
+
+  return old;
+}
+cpp_macro *cpp_get_deferred_macro (cpp_reader *, cpp_hashnode *, location_t);
 
 /* Returns true if NODE is a function-like user macro.  */
 inline bool cpp_fun_like_macro_p (cpp_hashnode *node)
@@ -1068,11 +1084,13 @@ inline bool cpp_fun_like_macro_p (cpp_hashnode *node)
   return cpp_user_macro_p (node) && node->value.macro->fun_like;
 }
 
-extern const unsigned char *cpp_macro_definition (cpp_reader *,
-                                                 cpp_hashnode *);
+extern const unsigned char *cpp_macro_definition (cpp_reader *, cpp_hashnode *);
+extern const unsigned char *cpp_macro_definition (cpp_reader *, cpp_hashnode *,
+                                                 const cpp_macro *);
 inline location_t cpp_macro_definition_location (cpp_hashnode *node)
 {
-  return node->value.macro->line;
+  const cpp_macro *macro = node->value.macro;
+  return macro ? macro->line : 0;
 }
 /* Return an idempotent time stamp (possibly from SOURCE_DATE_EPOCH).  */
 enum class CPP_time_kind 
@@ -1266,6 +1284,8 @@ extern int cpp_ideq (const cpp_token *, const char *);
 extern void cpp_output_line (cpp_reader *, FILE *);
 extern unsigned char *cpp_output_line_to_string (cpp_reader *,
                                                 const unsigned char *);
+extern const unsigned char *cpp_alloc_token_string
+  (cpp_reader *, const unsigned char *, unsigned);
 extern void cpp_output_token (const cpp_token *, FILE *);
 extern const char *cpp_type2name (enum cpp_ttype, unsigned char flags);
 /* Returns the value of an escape sequence, truncated to the correct
@@ -1321,6 +1341,8 @@ extern void cpp_scan_nooutput (cpp_reader *);
 extern int  cpp_sys_macro_p (cpp_reader *);
 extern unsigned char *cpp_quote_string (unsigned char *, const unsigned char *,
                                        unsigned int);
+extern bool cpp_compare_macros (const cpp_macro *macro1,
+                               const cpp_macro *macro2);
 
 /* In files.c */
 extern bool cpp_included (cpp_reader *, const char *);
index 697fef053ba5d126d4953768239cd34cca2a6498..45bbbddf2684428c2e1ec0e085bb344012b7016d 100644 (file)
@@ -662,13 +662,14 @@ inline bool _cpp_defined_macro_p (cpp_hashnode *node)
 }
 
 /* In macro.c */
-extern void _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
-                                  location_t loc);
-inline void _cpp_maybe_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
+extern bool _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
+                                  location_t);
+inline bool _cpp_maybe_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
                                         location_t loc)
 {
   if (!(node->flags & NODE_USED))
-    _cpp_notify_macro_use (pfile, node, loc);
+    return _cpp_notify_macro_use (pfile, node, loc);
+  return true;
 }
 extern cpp_macro *_cpp_new_macro (cpp_reader *, cpp_macro_kind, void *);
 extern void _cpp_free_definition (cpp_hashnode *);
index 0f18daf67642eb892e12f09bb1a09ae0d1f0bb9a..07d5a4ff4668853a230c00f53dd48b0691276b09 100644 (file)
@@ -1577,13 +1577,20 @@ static void
 create_literal (cpp_reader *pfile, cpp_token *token, const uchar *base,
                unsigned int len, enum cpp_ttype type)
 {
-  uchar *dest = _cpp_unaligned_alloc (pfile, len + 1);
-
-  memcpy (dest, base, len);
-  dest[len] = '\0';
   token->type = type;
   token->val.str.len = len;
-  token->val.str.text = dest;
+  token->val.str.text = cpp_alloc_token_string (pfile, base, len);
+}
+
+const uchar *
+cpp_alloc_token_string (cpp_reader *pfile,
+                       const unsigned char *ptr, unsigned len)
+{
+  uchar *dest = _cpp_unaligned_alloc (pfile, len + 1);
+
+  dest[len] = 0;
+  memcpy (dest, ptr, len);
+  return dest;
 }
 
 /* A pair of raw buffer pointers.  The currently open one is [1], the
@@ -2712,6 +2719,7 @@ cpp_maybe_module_directive (cpp_reader *pfile, cpp_token *result)
          /* Don't attempt to expand the token.  */
          tok->flags |= NO_EXPAND;
          if (_cpp_defined_macro_p (node)
+             && _cpp_maybe_notify_macro_use (pfile, node, tok->src_loc)
              && !cpp_fun_like_macro_p (node))
            cpp_error_with_line (pfile, CPP_DL_ERROR, tok->src_loc, 0, 
                                 "module control-line \"%s\" cannot be"
index 35a5e70851971aabd5ba94ee49d28e5b455b360a..05755859cd68d1782d9fdb28cc8e953cf845c008 100644 (file)
@@ -268,6 +268,8 @@ class vaopt_state {
 
 /* Macro expansion.  */
 
+static cpp_macro *get_deferred_or_lazy_macro (cpp_reader *, cpp_hashnode *,
+                                             location_t);
 static int enter_macro_context (cpp_reader *, cpp_hashnode *,
                                const cpp_token *, location_t);
 static int builtin_macro (cpp_reader *, cpp_hashnode *,
@@ -338,10 +340,6 @@ static cpp_macro *create_iso_definition (cpp_reader *);
 /* #define directive parsing and handling.  */
 
 static cpp_macro *lex_expansion_token (cpp_reader *, cpp_macro *);
-static bool warn_of_redefinition (cpp_reader *, cpp_hashnode *,
-                                 const cpp_macro *);
-static bool compare_macros (const cpp_macro *, const cpp_macro *);
-
 static bool parse_params (cpp_reader *, unsigned *, bool *);
 static void check_trad_stringification (cpp_reader *, const cpp_macro *,
                                        const cpp_string *);
@@ -353,8 +351,6 @@ static const cpp_token* cpp_get_token_1 (cpp_reader *, location_t *);
 
 static cpp_hashnode* macro_of_context (cpp_context *context);
 
-static bool in_macro_expansion_p (cpp_reader *pfile);
-
 /* Statistical counter tracking the number of macros that got
    expanded.  */
 unsigned num_expanded_macros_counter = 0;
@@ -2878,6 +2874,12 @@ cpp_get_token_1 (cpp_reader *pfile, location_t *location)
       if (node->type == NT_VOID || (result->flags & NO_EXPAND))
        break;
 
+      if (!(node->flags & NODE_USED)
+         && node->type == NT_USER_MACRO
+         && !node->value.macro
+         && !cpp_get_deferred_macro (pfile, node, result->src_loc))
+       break;
+
       if (!(node->flags & NODE_DISABLED))
        {
          int ret = 0;
@@ -3216,22 +3218,15 @@ warn_of_redefinition (cpp_reader *pfile, cpp_hashnode *node,
   if (node->flags & NODE_CONDITIONAL)
     return false;
 
-  cpp_macro *macro1 = node->value.macro;
-  if (macro1->lazy)
-    {
-      /* We don't want to mark MACRO as used, but do need to finalize
-        its laziness.  */
-      pfile->cb.user_lazy_macro (pfile, macro1, macro1->lazy - 1);
-      macro1->lazy = 0;
-    }
-
-  return compare_macros (macro1, macro2);
+  if (cpp_macro *macro1 = get_deferred_or_lazy_macro (pfile, node, macro2->line))
+    return cpp_compare_macros (macro1, macro2);
+  return false;
 }
 
 /* Return TRUE if MACRO1 and MACRO2 differ.  */
 
-static bool
-compare_macros (const cpp_macro *macro1, const cpp_macro *macro2)
+bool
+cpp_compare_macros (const cpp_macro *macro1, const cpp_macro *macro2)
 {
   /* Redefinition of a macro is allowed if and only if the old and new
      definitions are the same.  (6.10.3 paragraph 2).  */
@@ -3790,11 +3785,46 @@ cpp_define_lazily (cpp_reader *pfile, cpp_hashnode *node, unsigned num)
   macro->lazy = num + 1;
 }
 
+/* NODE is a deferred macro, resolve it, returning the definition
+   (which may be NULL).  */
+cpp_macro *
+cpp_get_deferred_macro (cpp_reader *pfile, cpp_hashnode *node,
+                       location_t loc)
+{
+  node->value.macro = pfile->cb.user_deferred_macro (pfile, loc, node);
+
+  if (!node->value.macro)
+    node->type = NT_VOID;
+
+  return node->value.macro;
+}
+
+static cpp_macro *
+get_deferred_or_lazy_macro (cpp_reader *pfile, cpp_hashnode *node,
+                           location_t loc)
+{
+  cpp_macro *macro = node->value.macro;
+  if (!macro)
+    {
+      macro = cpp_get_deferred_macro (pfile, node, loc);
+      if (!macro)
+       return NULL;
+    }
+
+  if (macro->lazy)
+    {
+      pfile->cb.user_lazy_macro (pfile, macro, macro->lazy - 1);
+      macro->lazy = 0;
+    }
+
+  return macro;
+}
+
 /* Notify the use of NODE in a macro-aware context (i.e. expanding it,
    or testing its existance).  Also applies any lazy definition.
    Return FALSE if the macro isn't really there.  */
 
-extern void
+extern bool
 _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
                       location_t loc)
 {
@@ -3802,14 +3832,8 @@ _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
   switch (node->type)
     {
     case NT_USER_MACRO:
-      {
-       cpp_macro *macro = node->value.macro;
-       if (macro->lazy)
-         {
-           pfile->cb.user_lazy_macro (pfile, macro, macro->lazy - 1);
-           macro->lazy = 0;
-         }
-      }
+      if (!get_deferred_or_lazy_macro (pfile, node, loc))
+       return false;
       /* FALLTHROUGH.  */
 
     case NT_BUILTIN_MACRO:
@@ -3825,6 +3849,8 @@ _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
     default:
       abort ();
     }
+
+  return true;
 }
 
 /* Warn if a token in STRING matches one of a function-like MACRO's
@@ -3877,12 +3903,19 @@ check_trad_stringification (cpp_reader *pfile, const cpp_macro *macro,
 const unsigned char *
 cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node)
 {
-  unsigned int i, len;
-  unsigned char *buffer;
-
   gcc_checking_assert (cpp_user_macro_p (node));
 
-  const cpp_macro *macro = node->value.macro;
+  if (const cpp_macro *macro = get_deferred_or_lazy_macro (pfile, node, 0))
+    return cpp_macro_definition (pfile, node, macro);
+  return NULL;
+}
+
+const unsigned char *
+cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node,
+                     const cpp_macro *macro)
+{
+  unsigned int i, len;
+  unsigned char *buffer;
 
   /* Calculate length.  */
   len = NODE_LEN (node) * 10 + 2;              /* ' ' and NUL.  */