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 <bunge...@gmail.com> * cppexp.c (eval_token): Update warning about defined. * gcc.dg/cpp/defined.c: Update. 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