diff mbox

[PP] Pedantic use of operator 'defined'.

Message ID 543FD995.5070301@gmail.com
State New
Headers show

Commit Message

Ben Wagner Oct. 16, 2014, 2:43 p.m. UTC
This is a partial revert (with update) of code removed in 46794 "Update
warning about defined." (This code was originally introduced with 37098
"New macro expander.")

My understanding of C++11 Specification Section 16.1 [cpp.cond] and C99
Specification Section 6.10.1 is that use of 'defined' from any macro
expansion is undefined behavior. While gcc is very permissive in this
regard and does what one would expect, a warning should be issued in
pedantic mode. Currently, gcc in pedantic mode will warn when a use of
'defined' is pieced together as the result of some macro expansion, but it
does not currently warn when an otherwise valid use of 'defined' is
created from a macro expansion.

This is not an entirely trivial exercise. There are a number of cases
where msvc 'exploits' this undefined behavior and may not work as
expected. The attached patch was developed specifically to find several
instances of this problem in several affected code bases.


2014-10-15  Ben Wagner  <bungeman@gmail.com>

        * cppexp.c (eval_token): Update warning about defined.
        * gcc.dg/cpp/defined.c: Update.
diff mbox

Patch

diff --git a/libcpp/expr.c b/libcpp/expr.c
index c24b640..9fd36d5 100644
--- a/libcpp/expr.c
+++ b/libcpp/expr.c
@@ -1049,7 +1049,14 @@  eval_token (cpp_reader *pfile, const cpp_token *token,

     case CPP_NAME:
       if (token->val.node.node == pfile->spec_nodes.n_defined)
-	return parse_defined (pfile);
+	{
+	  if (pfile->context->prev && CPP_PEDANTIC (pfile))
+	    cpp_error_with_line (pfile, CPP_DL_WARNING, virtual_location, 0,
+				 "this use of \"defined\" may not be portable");
+
+	  return parse_defined (pfile);
+	}
+
       else if (token->val.node.node == pfile->spec_nodes.n__has_include__)
 	return parse_has_include (pfile, IT_INCLUDE);
       else if (token->val.node.node == pfile->spec_nodes.n__has_include_next__)

diff --git a/gcc/testsuite/gcc.dg/cpp/defined.c b/gcc/testsuite/gcc.dg/cpp/defined.c
index 9a60bdd..6096e85 100644
--- a/gcc/testsuite/gcc.dg/cpp/defined.c
+++ b/gcc/testsuite/gcc.dg/cpp/defined.c
@@ -1,6 +1,7 @@ 
 /* Copyright (C) 2000 Free Software Foundation, Inc.  */

 /* { dg-do preprocess } */
+/* { dg-additional-options "-ftrack-macro-expansion=2" } */

 /* Tests behavior of the defined operator.  */

@@ -13,15 +14,15 @@ 
 #error defined is defined!
 #endif

-#define is_Z_defined defined Z
+#define is_Z_defined defined Z	/* { dg-warning "may not be portable" } */

 #if defined Z
 #error Z is not defined
 #endif

-/* The behavior of "defined" when it comes from a macro expansion is
-   now documented.  */
-#if is_Z_defined
+/* The behavior of "defined" when it comes from a macro expansion is documented
+   by gcc, but still undefined according to the specification.  */
+#if is_Z_defined		/* { dg-message "is_Z_defined" } */
 #error Macro expanding into defined operator test 1
 #endif

@@ -31,7 +32,7 @@ 
 #error Z is defined
 #endif

-#if !is_Z_defined
+#if !is_Z_defined		/* { dg-message "is_Z_defined" } */
 #error Macro expanding into defined operator test 2
 #endif

@@ -45,15 +46,15 @@ 
 #error defined is defined!
 #endif

-#define is_Z_defined defined ( Z )
+#define is_Z_defined defined ( Z )    /* { dg-warning "may not be portable" } */

 #if defined(Z)
 #error Z is not defined
 #endif

-/* The behavior of "defined" when it comes from a macro expansion is
-   now documented.  */
-#if is_Z_defined
+/* The behavior of "defined" when it comes from a macro expansion is documented
+   by gcc, but still undefined according to the specification.  */
+#if is_Z_defined		/* { dg-message "is_Z_defined" } */
 #error Macro expanding into defined operator test 1
 #endif

@@ -63,14 +64,15 @@ 
 #error Z is defined
 #endif

-#if !is_Z_defined
+#if !is_Z_defined		/* { dg-message "is_Z_defined" } */
 #error Macro expanding into defined operator test 2
 #endif

 /* Use of defined in different contexts.  */

-#define bad1 defined
+#define bad1 defined            /* { dg-warning "may not be portable" } */
 #if !bad1 Z			/* { dg-warning "may not be portable" } */
+		          /* { dg-message "bad1" "note" { target *-*-* } 74 } */
 #error Z is defined
 #endif

@@ -78,8 +80,9 @@ 
 #error Z is defined
 #endif

-#define bad2 defined (Z
+#define bad2 defined (Z		/* { dg-warning "may not be portable" } */
 #if !bad2)			/* { dg-warning "may not be portable" } */
+			  /* { dg-message "bad2" "note" { target *-*-* } 84 } */
 #error Z is defined
 #endif