diff mbox series

preprocessor: Add deferred macros

Message ID 75547dcf-ca26-0a65-2501-116a6ba69641@acm.org
State New
Headers show
Series preprocessor: Add deferred macros | expand

Commit Message

Nathan Sidwell Nov. 24, 2020, 4:30 p.m. UTC
this completes the libcpp changes for C++ modules.  It is slightly 
different to the patch I posted earlier, as I discovered I'd added a 
bitfield I didn't end up using.  so that's now removed.

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.

pushing to trunk

nathan
diff mbox series

Patch

diff --git i/libcpp/directives.c w/libcpp/directives.c
index bffdc913adb..fa66b5c5f71 100644
--- i/libcpp/directives.c
+++ w/libcpp/directives.c
@@ -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);
diff --git i/libcpp/expr.c w/libcpp/expr.c
index b98c0386eb5..2ba7726d61c 100644
--- i/libcpp/expr.c
+++ w/libcpp/expr.c
@@ -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;
 }
 
diff --git i/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
index 91226cfc248..2becd2e8e54 100644
--- i/libcpp/include/cpplib.h
+++ w/libcpp/include/cpplib.h
@@ -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 *);
diff --git i/libcpp/internal.h w/libcpp/internal.h
index 697fef053ba..45bbbddf268 100644
--- i/libcpp/internal.h
+++ w/libcpp/internal.h
@@ -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 *);
diff --git i/libcpp/lex.c w/libcpp/lex.c
index 0f18daf6764..07d5a4ff466 100644
--- i/libcpp/lex.c
+++ w/libcpp/lex.c
@@ -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"
diff --git i/libcpp/macro.c w/libcpp/macro.c
index 35a5e708519..05755859cd6 100644
--- i/libcpp/macro.c
+++ w/libcpp/macro.c
@@ -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.  */