3.2 cpplib: Permit directives in macro args

Neil Booth neil@daikokuya.demon.co.uk
Wed Feb 27 00:26:00 GMT 2002


Joseph S. Myers wrote:-

> pedwarns if pedantic are fine for compile-time undefined behavior as well
> as explicit constraint violations.  -pedantic is what GCC uses for
> portability testing, and how preprocessor conditionals inside strcmp (say)  
> should be detected if they make their way into GCC, rather than requiring
> yet another option - in general -pedantic should give warnings for use of
> extensions.
 
OK, I'll warn iff -pedantic, and add a testcase.  The patch below has
built and regtested

> If you want to warn about this extension at -W, then you shouldn't have it
> at all - the extension should only be present if it can be justified as
> useful.  I suspect the particular case of #undef of a macro while
> expanding it is especially unlikely to be useful and disallowing would be
> better than defining semantics for it.

The idea is to have stuff behave the same whether or not a function is
implemented as a macro or function, so I prefer to keep the semantics.
Plus they're the ones that drop out of the implementation naturally.

Neil.

	* cpplex.c (_cpp_lex_token): Handle directives in macro
	arguments.
	* cpplib.c (_cpp_handle_directive): Save and restore state
	if parsing macro args when entering a directive.
	* cppmacro.c (collect_args): No need to handle directives
	in macro arguments.
	(enter_macro_context, replace_args): Use the original macro
	definition in case it was redefined whilst collecting arguments.
doc:
	* cpp.texi: Update.
testsuite:
	* gcc.dg/cpp/undef1.c: Remove.
	* gcc.dg/cpp/directiv.c: Update.
	* gcc.dg/cpp/mac-dir-1.c, mac-dir-2.c: New tests.

============================================================
Index: gcc/cpplex.c
--- gcc/cpplex.c	2002/02/24 12:52:21	1.188
+++ gcc/cpplex.c	2002/02/27 07:07:27
@@ -828,7 +828,10 @@ _cpp_lex_token (pfile)
 	  /* Is this a directive.  If _cpp_handle_directive returns
 	     false, it is an assembler #.  */
 	  if (result->type == CPP_HASH
-	      && !pfile->state.parsing_args
+	      /* 6.10.3 p 11: Directives in a list of macro arguments
+		 gives undefined behavior.  This implementation
+		 handles the directive as normal.  */
+	      && pfile->state.parsing_args != 1
 	      && _cpp_handle_directive (pfile, result->flags & PREV_WHITE))
 	    continue;
 	  if (pfile->cb.line_change && !pfile->state.skipping)
============================================================
Index: gcc/cpplib.c
--- gcc/cpplib.c	2002/02/23 20:21:39	1.290
+++ gcc/cpplib.c	2002/02/27 07:07:34
@@ -316,8 +316,17 @@ _cpp_handle_directive (pfile, indented)
 {
   const directive *dir = 0;
   const cpp_token *dname;
+  bool was_parsing_args = pfile->state.parsing_args;
   int skip = 1;
 
+  if (was_parsing_args)
+    {
+      if (CPP_OPTION (pfile, pedantic))
+	cpp_pedwarn (pfile,
+	     "embedding a directive within macro arguments is not portable");
+      pfile->state.parsing_args = 0;
+      pfile->state.prevent_expansion = 0;
+    }
   start_directive (pfile);
   dname = _cpp_lex_token (pfile);
 
@@ -393,6 +402,13 @@ _cpp_handle_directive (pfile, indented)
     _cpp_backup_tokens (pfile, 1);
 
   end_directive (pfile, skip);
+  if (was_parsing_args)
+    {
+      /* Restore state when within macro args.  */
+      pfile->state.parsing_args = 2;
+      pfile->state.prevent_expansion = 1;
+      pfile->buffer->saved_flags |= PREV_WHITE;
+    }
   return skip;
 }
 
============================================================
Index: gcc/cppmacro.c
--- gcc/cppmacro.c	2002/01/03 21:43:05	1.91
+++ gcc/cppmacro.c	2002/02/27 07:07:37
@@ -74,7 +74,8 @@ static const cpp_token *stringify_arg PA
 static void paste_all_tokens PARAMS ((cpp_reader *, const cpp_token *));
 static bool paste_tokens PARAMS ((cpp_reader *, const cpp_token **,
 				  const cpp_token *));
-static void replace_args PARAMS ((cpp_reader *, cpp_hashnode *, macro_arg *));
+static void replace_args PARAMS ((cpp_reader *, cpp_hashnode *, cpp_macro *,
+				  macro_arg *));
 static _cpp_buff *funlike_invocation_p PARAMS ((cpp_reader *, cpp_hashnode *));
 
 /* #define directive parsing and handling.  */
@@ -546,34 +547,15 @@ collect_args (pfile, node)
 	    arg++;
 	}
     }
-  while (token->type != CPP_CLOSE_PAREN
-	 && token->type != CPP_EOF
-	 && token->type != CPP_HASH);
+  while (token->type != CPP_CLOSE_PAREN && token->type != CPP_EOF);
 
-  if (token->type == CPP_EOF || token->type == CPP_HASH)
+  if (token->type == CPP_EOF)
     {
-      bool step_back = false;
-
-      /* 6.10.3 paragraph 11: If there are sequences of preprocessing
-	 tokens within the list of arguments that would otherwise act
-	 as preprocessing directives, the behavior is undefined.
-
-	 This implementation will report a hard error, terminate the
-	 macro invocation, and proceed to process the directive.  */
-      if (token->type == CPP_HASH)
-	{
-	  cpp_error (pfile,
-		     "directives may not be used inside a macro argument");
-	  step_back = true;
-	}
-      else
-	step_back = (pfile->context->prev || pfile->state.in_directive);
-
       /* We still need the CPP_EOF to end directives, and to end
 	 pre-expansion of a macro argument.  Step back is not
 	 unconditional, since we don't want to return a CPP_EOF to our
 	 callers at the end of an -include-d file.  */
-      if (step_back)
+      if (pfile->context->prev || pfile->state.in_directive)
 	_cpp_backup_tokens (pfile, 1);
       cpp_error (pfile, "unterminated argument list invoking macro \"%s\"",
 		 NODE_NAME (node));
@@ -697,8 +679,8 @@ enter_macro_context (pfile, node)
 	      return 0;
 	    }
 
-	  if (node->value.macro->paramc > 0)
-	    replace_args (pfile, node, (macro_arg *) buff->base);
+	  if (macro->paramc > 0)
+	    replace_args (pfile, node, macro, (macro_arg *) buff->base);
 	  _cpp_release_buff (pfile, buff);
 	}
 
@@ -720,9 +702,10 @@ enter_macro_context (pfile, node)
    Expand each argument before replacing, unless it is operated upon
    by the # or ## operators.  */
 static void
-replace_args (pfile, node, args)
+replace_args (pfile, node, macro, args)
      cpp_reader *pfile;
      cpp_hashnode *node;
+     cpp_macro *macro;
      macro_arg *args;
 {
   unsigned int i, total;
@@ -730,13 +713,11 @@ replace_args (pfile, node, args)
   const cpp_token **dest, **first;
   macro_arg *arg;
   _cpp_buff *buff;
-  cpp_macro *macro;
 
   /* First, fully macro-expand arguments, calculating the number of
      tokens in the final expansion as we go.  The ordering of the if
      statements below is subtle; we must handle stringification before
      pasting.  */
-  macro = node->value.macro;
   total = macro->count;
   limit = macro->expansion + macro->count;
 
============================================================
Index: gcc/doc/cpp.texi
--- gcc/doc/cpp.texi	2002/02/23 17:10:29	1.24
+++ gcc/doc/cpp.texi	2002/02/27 07:07:51
@@ -121,6 +121,7 @@ Macros
 * Variadic Macros::
 * Predefined Macros::
 * Undefining and Redefining Macros::
+* Directives Within Macro Arguments::
 * Macro Pitfalls::
 
 Predefined Macros
@@ -1115,6 +1116,7 @@ macros when you are compiling C++.
 * Variadic Macros::
 * Predefined Macros::
 * Undefining and Redefining Macros::
+* Directives Within Macro Arguments::
 * Macro Pitfalls::
 @end menu
 
@@ -2115,6 +2117,48 @@ macro to use the new definition.  If the
 the same, the redefinition is silently ignored.  This allows, for
 instance, two different headers to define a common macro.  The
 preprocessor will only complain if the definitions do not match.
+
+@node Directives Within Macro Arguments
+@section Directives Within Macro Arguments
+@cindex macro arguments and directives
+
+Occasionally it is convenient to use preprocessor directives within
+the arguments of a macro.  The C and C++ standards declare that
+behavior in these cases is undefined.
+
+Versions of GNU CPP prior to 3.2 would reject such constructs with an
+error message.  This was the only syntactic difference between normal
+functions and function-like macros, so it seemed attractive to remove
+this limitation, and people would often be surprised that they could
+not use macros in this way.  Moreover, sometimes people would use
+conditional compilation in the argument list to a normal library
+function like @samp{printf}, only to find that after a library upgrade
+@samp{printf} had changed to be a function-like macro, and their code
+would no longer compile.  So from version 3.2 we changed CPP to
+successfully process arbitrary directives within macro arguments in
+exactly the same way as it would have processed the directive were the
+function-like macro invocation not present.
+
+If, within a macro invocation, that macro is redefined, then the new
+definition takes effect in time for argument pre-expansion, but the
+original definition is still used for argument replacement.  Here is a
+pathological example:
+
+@smallexample
+#define f(x) x x
+f (1
+#undef f
+#define f 2
+f)
+@end smallexample
+
+@noindent which expands to
+
+@smallexample
+1 2 1 2
+@end smallexample
+
+@noindent with the semantics described above.
 
 @node Macro Pitfalls
 @section Macro Pitfalls
============================================================
Index: gcc/testsuite/gcc.dg/cpp/directiv.c
--- gcc/testsuite/gcc.dg/cpp/directiv.c	2001/09/13 20:05:17	1.5
+++ gcc/testsuite/gcc.dg/cpp/directiv.c	2002/02/27 07:07:53
@@ -25,16 +25,11 @@ EMPTY #define bar
 /* Our friend the null directive OK?  */
 #
 
-/* Check that directives always start a line, even if in middle of
-   macro expansion.  */
-#define func(x) x
-func (2		/* { dg-error "unterminated" "" } */
-#define foobar	/* { dg-error "directives may not" } */
-
 /* Check newlines end directives, even in function-like macro
    invocations.  6.10 paragraph 1.
 
    Note that the #if is still treated as a conditional, so there
    should be no errors about #endif without #if.  */
+#define func(x) x
 #if func (			/* { dg-error "unterminated argument" } */
 #endif
============================================================
Index: gcc/testsuite/gcc.dg/cpp/mac-dir-1.c
--- /dev/null	Tue May  5 13:32:27 1998
+++ gcc/testsuite/gcc.dg/cpp/mac-dir-1.c	Tue Feb 26 23:07:53 2002
@@ -0,0 +1,34 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.  */
+
+/* { dg-do preprocess } */
+
+/* Source: Neil Booth, 26 Feb 2002.
+
+   Test that we allow directives in macro arguments.  */
+
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#define f(x) x
+extern void abort (void);
+
+int main ()
+{
+  if (f (
+#if f(1)			/* True.  */
+	0))			/* False. */
+#else
+    	1))
+#endif
+     abort ();
+
+     /* Outer f expands to original definition, f in argument expands
+	to new definition, so result is: if (1 != 2 - 1).  */
+     if (1 != f(2
+#undef f
+#define f - 1
+     f))
+     abort ();
+
+     return 0;
+}
============================================================
Index: gcc/testsuite/gcc.dg/cpp/mac-dir-2.c
--- /dev/null	Tue May  5 13:32:27 1998
+++ gcc/testsuite/gcc.dg/cpp/mac-dir-2.c	Tue Feb 26 23:07:53 2002
@@ -0,0 +1,16 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.  */
+
+/* { dg-do preprocess } */
+
+/* Source: Neil Booth, 26 Feb 2002.
+
+   Test that we allow directives in macro arguments.  */
+
+/* { dg-do preprocess } */
+
+#define f(x) x
+
+f (
+#if 1		/* { dg-warning "not portable" } */
+1)
+#endif
============================================================
Index: gcc/testsuite/gcc.dg/cpp/undef1.c
--- /sourceware/cvs-tmp/cvsfXPFIj	Tue Feb 26 23:07:57 2002
+++ /dev/null	Tue May  5 13:32:27 1998
@@ -1,14 +0,0 @@
-/* { dg-do preprocess } */
-
-/* 6.9.3.11: ...If there  are sequences of preprocessing tokens within
-   the list of arguments  that  would  otherwise  act  as  preprocessing
-   directives, the behavior is undefined.
-
-   I choose to make this a hard error.  It definitely should not cause
-   a core dump.  */
-
-#define foo(bar) bar
-
-foo( blah	/* { dg-error "unterminated" "" } */
-#undef foo	/* { dg-error "may not be used inside" "foo(#undef foo)" } */
-     blah )



More information about the Gcc-patches mailing list