diff mbox series

[PR,80005] __has_include parsing

Message ID 7bf558d3-9eea-b043-f37c-477e715ea7e3@acm.org
State New
Headers show
Series [PR,80005] __has_include parsing | expand

Commit Message

Nathan Sidwell Jan. 20, 2020, 1:44 p.m. UTC
I've pushed this to master, to address 80005

__has_include is funky in that it is macro-like from the POV of #ifdef 
and friends, but lexes its parenthesize argument #include-like.  We were 
failing the second part of that, because we used a forwarding macro to 
an internal name, and hence always lexed the argument in macro-parameter 
context.  We componded that by not setting the right flag when lexing, 
so it didn't even know.  Mostly users got lucky.

This reimplements the handline.
1) Remove the forwarding, but declare object-like macros that expand to 
themselves.  This satisfies the #ifdef requirement

2) Correctly set angled_brackets when lexing the parameter.  This tells 
the lexer (a) <...> is a header name and (b) "..." is too (not a string).

3) Remove the in__has_include lexer state, just tell find_file that 
that's what's happenning, so it doesn't emit an error.

We lose the (undocumented) ability to #undef __has_include.  That may 
well have been an accident of implementation.  There are no tests for it.

We gain __has_include behaviour for all users of the preprocessors -- 
not just the C-family ones that defined a forwarding macro.

nathan

Comments

Jim Wilson Jan. 21, 2020, 2:01 a.m. UTC | #1
On Mon, Jan 20, 2020 at 5:44 AM Nathan Sidwell <nathan@acm.org> wrote:
> I've pushed this to master, to address 80005
>
> __has_include is funky in that it is macro-like from the POV of #ifdef
> ...

With this patch, __has_include__ no longer works.  There is a use of
this in the RISC-V glibc port.  I see the docs only mention
__has_include, so maybe this has always been a silent bug in the
RISC-V glibc port?  If glibc is broken I can submit a patch.  This
might cause trouble for other people too though.

Given the testcase,
#if __has_include__ (<stdio.h>)
#error "yes"
#else
#error "no"
#endif

#if __has_include (<stdio.h>)
#error "yes"
#else
#error "no"
#endif

Without this patch I get
rohan:2212$ ./xgcc -B./ -O -S tmp4.c
tmp4.c:2:2: error: #error "yes"
    2 | #error "yes"
      |  ^~~~~
tmp4.c:8:2: error: #error "yes"
    8 | #error "yes"
      |  ^~~~~

and with this patch I get
gamma05:2206$ build-install/bin/riscv64-unknown-linux-gnu-gcc -O -S tmp4.c
tmp4.c:1:21: error: missing binary operator before token "("
    1 | #if __has_include__ (<stdio.h>)
      |                     ^
tmp4.c:4:2: error: #error "no"
    4 | #error "no"
      |  ^~~~~
tmp4.c:8:2: error: #error "yes"
    8 | #error "yes"
      |  ^~~~~

Jim
Jakub Jelinek Jan. 21, 2020, 8:22 a.m. UTC | #2
On Mon, Jan 20, 2020 at 06:01:27PM -0800, Jim Wilson wrote:
> On Mon, Jan 20, 2020 at 5:44 AM Nathan Sidwell <nathan@acm.org> wrote:
> > I've pushed this to master, to address 80005
> >
> > __has_include is funky in that it is macro-like from the POV of #ifdef
> > ...
> 
> With this patch, __has_include__ no longer works.  There is a use of
> this in the RISC-V glibc port.  I see the docs only mention
> __has_include, so maybe this has always been a silent bug in the
> RISC-V glibc port?  If glibc is broken I can submit a patch.  This
> might cause trouble for other people too though.

I only see one spot where it has been added and then
2019-06-06  Florian Weimer  <fweimer@redhat.com>

        * sysdeps/unix/sysv/linux/riscv/flush-icache.c: Do not use
        internal GCC preprocessor identifier __has_include__.
which corrected it to use __has_include.

	Jakub
Nathan Sidwell Jan. 21, 2020, 12:03 p.m. UTC | #3
On 1/20/20 9:01 PM, Jim Wilson wrote:
> On Mon, Jan 20, 2020 at 5:44 AM Nathan Sidwell <nathan@acm.org> wrote:
>> I've pushed this to master, to address 80005
>>
>> __has_include is funky in that it is macro-like from the POV of #ifdef
>> ...
> 
> With this patch, __has_include__ no longer works.  There is a use of
> this in the RISC-V glibc port.  I see the docs only mention
> __has_include, so maybe this has always been a silent bug in the
> RISC-V glibc port?  If glibc is broken I can submit a patch.  This
> might cause trouble for other people too though.

tl;dr; glibc is broken.

AFAICT __has_include__ was an undocumented piece of internal 
implementation goop, that didn't really work anyway.  I'm sure it's 
presence has confused users.

I guess I have to add compatibility ...

> and with this patch I get
> gamma05:2206$ build-install/bin/riscv64-unknown-linux-gnu-gcc -O -S tmp4.c
> tmp4.c:1:21: error: missing binary operator before token "("
>      1 | #if __has_include__ (<stdio.h>)

yup, 'cos as I'm sure you're aware __has_include__ will now be a regular 
identifier, and treated as a literal '0' in this context.

nathan
Jim Wilson Jan. 21, 2020, 4:50 p.m. UTC | #4
On Tue, Jan 21, 2020 at 12:22 AM Jakub Jelinek <jakub@redhat.com> wrote:
> I only see one spot where it has been added and then
> 2019-06-06  Florian Weimer  <fweimer@redhat.com>
>
>         * sysdeps/unix/sysv/linux/riscv/flush-icache.c: Do not use
>         internal GCC preprocessor identifier __has_include__.
> which corrected it to use __has_include.

OK, so upstream glibc is already fixed which is good, but there is at
least one glibc release with the bug, and some trees like
github.com/riscv/riscv-gnu-toolchain that have the broken glibc
sources.  I can deal with that problem though.  We will probably
upgrade glibc when we upgrade gcc anyways.

Jim
diff mbox series

Patch

2020-01-20  Nathan Sidwell  <nathan@acm.org>

	libcpp/
	PR preprocessor/80005
	* include/cpplib.h (BT_HAS_ATTRIBUTE): Fix comment.
	* internal.h (struct lexer_state): Delete in__has_include field.
	(struct spec_nodes): Rename n__has_include{,_next}__ fields.
	(_cpp_defined_macro_p): New.
	(_cpp_find_file): Add has_include parm.
	* directives.c (lex_macro_node): Combine defined,
	__has_inline{,_next} checking.
	(do_ifdef, do_ifndef): Use _cpp_defined_macro_p.
	(_cpp_init_directives): Refactor.
	* expr.c (parse_defined): Use _cpp_defined_macro_p.
	(eval_token): Adjust parse_has_include calls.
	(parse_has_include): Add OP parameter.  Reimplement.
	* files.c (_cpp_find_file): Add HAS_INCLUDE parm.  Use it to
	inhibit error message.
	(_cpp_stack_include): Adjust _cpp_find_file call.
	(_cpp_fake_include, _cpp_compare_file_date): Likewise.
	(open_file_failed): Remove in__has_include check.
	(_cpp_has_header): Adjust _cpp_find_file call.
	* identifiers.c (_cpp_init_hashtable): Don't init
	__has_include{,_next} here ...
	* init.c (cpp_init_builtins): ... init them here.  Define as
	macros.
	(cpp_read_main_file): Adjust _cpp_find_file call.
	* pch.c (cpp_read_state): Adjust __has_include{,_next} access.
	* traditional.c (_cpp_scan_out_locgical_line): Likewise.

	gcc/c-family/
	PR preprocessor/80005
	* c-cppbuiltins.c (c_cpp_builtins): Don't define __has_include{,_next}.

	gcc/testsuite/
	PR preprocessor/80005
	* g++.dg/cpp1y/feat-cxx14.C: Adjust.
	* g++.dg/cpp1z/feat-cxx17.C: Adjust.
	* g++.dg/cpp2a/feat-cxx2a.C: Adjust.
	* g++.dg/cpp/pr80005.C: New.

diff --git c/gcc/c-family/c-cppbuiltin.c w/gcc/c-family/c-cppbuiltin.c
index a6308921dc9..70a12055e27 100644
--- c/gcc/c-family/c-cppbuiltin.c
+++ w/gcc/c-family/c-cppbuiltin.c
@@ -876,12 +876,6 @@  c_cpp_builtins (cpp_reader *pfile)
   /* For stddef.h.  They require macros defined in c-common.c.  */
   c_stddef_cpp_builtins ();
 
-  /* Set include test macros for all C/C++ (not for just C++11 etc.)
-     The builtins __has_include__ and __has_include_next__ are defined
-     in libcpp.  */
-  cpp_define (pfile, "__has_include(STR)=__has_include__(STR)");
-  cpp_define (pfile, "__has_include_next(STR)=__has_include_next__(STR)");
-
   if (c_dialect_cxx ())
     {
       if (flag_weak && SUPPORTS_ONE_ONLY)
diff --git c/gcc/testsuite/g++.dg/cpp/pr80005.C w/gcc/testsuite/g++.dg/cpp/pr80005.C
new file mode 100644
index 00000000000..cc752616782
--- /dev/null
+++ w/gcc/testsuite/g++.dg/cpp/pr80005.C
@@ -0,0 +1,24 @@ 
+// PR preprocessor/80005
+// { dg-do preprocess }
+
+#undef vector
+#define vector NOPE
+#ifdef __has_include
+
+#if !__has_include (<vector>)
+#error "Header 'vector' could not be found"
+#endif
+#define F(X) __has_include (X)
+#if !F (<vector>)
+#error "Header 'vector' could not be found" // { dg-error "not be found" }
+#endif
+
+#if __has_include ("not an escape! \")	// comment
+#endif
+
+#if F ("is an escape \") gibberish ")
+#endif
+
+#else
+#error "No __has_include"
+#endif
diff --git c/gcc/testsuite/g++.dg/cpp1y/feat-cxx14.C w/gcc/testsuite/g++.dg/cpp1y/feat-cxx14.C
index a2a93f437b3..a78b6a36f36 100644
--- c/gcc/testsuite/g++.dg/cpp1y/feat-cxx14.C
+++ w/gcc/testsuite/g++.dg/cpp1y/feat-cxx14.C
@@ -236,20 +236,14 @@ 
 #  error "__has_include"
 #endif
 
-//  Quoted complex.h should find at least the bracket version (use operator).
-#if __has_include__ "complex.h"
-#else
-#  error "complex.h"
-#endif
-
 //  Try known bracket header (use operator).
-#if __has_include__(<complex>)
+#if __has_include (<complex>)
 #else
 #  error "<complex>"
 #endif
 
 //  Define and use a macro to invoke the operator.
-#define sluggo(TXT) __has_include__(TXT)
+#define sluggo(TXT) __has_include(TXT)
 
 #if sluggo(<complex>)
 #else
diff --git c/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C w/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
index 55e56a06fe8..e6f456b2415 100644
--- c/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
+++ w/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
@@ -225,20 +225,14 @@ 
 #  error "__has_include"
 #endif
 
-//  Quoted complex.h should find at least the bracket version (use operator).
-#if __has_include__ "complex.h"
-#else
-#  error "complex.h"
-#endif
-
 //  Try known bracket header (use operator).
-#if __has_include__(<complex>)
+#if __has_include (<complex>)
 #else
 #  error "<complex>"
 #endif
 
 //  Define and use a macro to invoke the operator.
-#define sluggo(TXT) __has_include__(TXT)
+#define sluggo(TXT) __has_include(TXT)
 
 #if sluggo(<complex>)
 #else
diff --git c/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C w/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index dd15cd6af3c..82fd602f9f1 100644
--- c/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ w/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -224,20 +224,14 @@ 
 #  error "__has_include"
 #endif
 
-//  Quoted complex.h should find at least the bracket version (use operator).
-#if __has_include__ "complex.h"
-#else
-#  error "complex.h"
-#endif
-
 //  Try known bracket header (use operator).
-#if __has_include__(<complex>)
+#if __has_include (<complex>)
 #else
 #  error "<complex>"
 #endif
 
 //  Define and use a macro to invoke the operator.
-#define sluggo(TXT) __has_include__(TXT)
+#define sluggo(TXT) __has_include(TXT)
 
 #if sluggo(<complex>)
 #else
diff --git c/libcpp/directives.c w/libcpp/directives.c
index 983206a5838..10735c8c668 100644
--- c/libcpp/directives.c
+++ w/libcpp/directives.c
@@ -595,14 +595,13 @@  lex_macro_node (cpp_reader *pfile, bool is_def_or_undef)
     {
       cpp_hashnode *node = token->val.node.node;
 
-      if (is_def_or_undef && node == pfile->spec_nodes.n_defined)
+      if (is_def_or_undef
+	  && (node == pfile->spec_nodes.n_defined
+	      || node == pfile->spec_nodes.n__has_include
+	      || node == pfile->spec_nodes.n__has_include_next))
 	cpp_error (pfile, CPP_DL_ERROR,
-		   "\"defined\" cannot be used as a macro name");
-      else if (is_def_or_undef
-	    && (node == pfile->spec_nodes.n__has_include__
-	     || node == pfile->spec_nodes.n__has_include_next__))
-	cpp_error (pfile, CPP_DL_ERROR,
-		   "\"__has_include__\" cannot be used as a macro name");
+		   "\"%s\" cannot be used as a macro name",
+		   NODE_NAME (node));
       else if (! (node->flags & NODE_POISONED))
 	return node;
     }
@@ -1966,11 +1965,7 @@  do_ifdef (cpp_reader *pfile)
 
       if (node)
 	{
-	  /* Do not treat conditional macros as being defined.  This is due to
-	     the powerpc port using conditional macros for 'vector', 'bool',
-	     and 'pixel' to act as conditional keywords.  This messes up tests
-	     like #ifndef bool.  */
-	  skip = !cpp_macro_p (node) || (node->flags & NODE_CONDITIONAL);
+	  skip = !_cpp_defined_macro_p (node);
 	  _cpp_mark_macro_used (node);
 	  _cpp_maybe_notify_macro_use (pfile, node);
 	  if (pfile->cb.used)
@@ -1999,8 +1994,7 @@  do_ifndef (cpp_reader *pfile)
 	     the powerpc port using conditional macros for 'vector', 'bool',
 	     and 'pixel' to act as conditional keywords.  This messes up tests
 	     like #ifndef bool.  */
-	  skip = (cpp_macro_p (node)
-		  && !(node->flags & NODE_CONDITIONAL));
+	  skip = _cpp_defined_macro_p (node);
 	  _cpp_mark_macro_used (node);
 	  _cpp_maybe_notify_macro_use (pfile, node);
 	  if (pfile->cb.used)
@@ -2638,12 +2632,9 @@  _cpp_pop_buffer (cpp_reader *pfile)
 void
 _cpp_init_directives (cpp_reader *pfile)
 {
-  unsigned int i;
-  cpp_hashnode *node;
-
-  for (i = 0; i < (unsigned int) N_DIRECTIVES; i++)
+  for (int i = 0; i < N_DIRECTIVES; i++)
     {
-      node = cpp_lookup (pfile, dtable[i].name, dtable[i].length);
+      cpp_hashnode *node = cpp_lookup (pfile, dtable[i].name, dtable[i].length);
       node->is_directive = 1;
       node->directive_index = i;
     }
diff --git c/libcpp/expr.c w/libcpp/expr.c
index 317faf50208..df21a4b9fb9 100644
--- c/libcpp/expr.c
+++ w/libcpp/expr.c
@@ -64,7 +64,7 @@  static unsigned int interpret_float_suffix (cpp_reader *, const uchar *, size_t)
 static unsigned int interpret_int_suffix (cpp_reader *, const uchar *, size_t);
 static void check_promotion (cpp_reader *, const struct op *);
 
-static cpp_num parse_has_include (cpp_reader *, enum include_type);
+static cpp_num parse_has_include (cpp_reader *, cpp_hashnode *, include_type);
 
 /* Token type abuse to create unary plus and minus operators.  */
 #define CPP_UPLUS ((enum cpp_ttype) (CPP_LAST_CPP_OP + 1))
@@ -1088,8 +1088,7 @@  parse_defined (cpp_reader *pfile)
   result.unsignedp = false;
   result.high = 0;
   result.overflow = false;
-  result.low = (node && cpp_macro_p (node)
-		&& !(node->flags & NODE_CONDITIONAL));
+  result.low = node && _cpp_defined_macro_p (node);
   return result;
 }
 
@@ -1160,10 +1159,10 @@  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);
-      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__)
-	return parse_has_include (pfile, IT_INCLUDE_NEXT);
+      else if (token->val.node.node == pfile->spec_nodes.n__has_include)
+	return parse_has_include (pfile, token->val.node.node, IT_INCLUDE);
+      else if (token->val.node.node == pfile->spec_nodes.n__has_include_next)
+	return parse_has_include (pfile, token->val.node.node, IT_INCLUDE_NEXT);
       else if (CPP_OPTION (pfile, cplusplus)
 	       && (token->val.node.node == pfile->spec_nodes.n_true
 		   || token->val.node.node == pfile->spec_nodes.n_false))
@@ -2190,9 +2189,9 @@  num_div_op (cpp_reader *pfile, cpp_num lhs, cpp_num rhs, enum cpp_ttype op,
   return lhs;
 }
 
-/* Handle meeting "__has_include__" in a preprocessor expression.  */
+/* Handle meeting "__has_include" in a preprocessor expression.  */
 static cpp_num
-parse_has_include (cpp_reader *pfile, enum include_type type)
+parse_has_include (cpp_reader *pfile, cpp_hashnode *op, include_type type)
 {
   cpp_num result;
 
@@ -2201,12 +2200,15 @@  parse_has_include (cpp_reader *pfile, enum include_type type)
   result.overflow = false;
   result.low = 0;
 
-  pfile->state.in__has_include__++;
-
+  pfile->state.angled_headers = true;
   const cpp_token *token = cpp_get_token (pfile);
   bool paren = token->type == CPP_OPEN_PAREN;
   if (paren)
     token = cpp_get_token (pfile);
+  else
+    cpp_error (pfile, CPP_DL_ERROR,
+	       "missing '(' before \"%s\" operand", NODE_NAME (op));
+  pfile->state.angled_headers = false;
 
   bool bracket = token->type != CPP_STRING;
   cpp_hashnode *node = NULL;
@@ -2222,7 +2224,7 @@  parse_has_include (cpp_reader *pfile, enum include_type type)
     fname = _cpp_bracket_include (pfile);
   else
     cpp_error (pfile, CPP_DL_ERROR,
-	       "operator \"__has_include__\" requires a header string");
+	       "operator \"%s\" requires a header-name", NODE_NAME (op));
 
   if (fname)
     {
@@ -2237,14 +2239,10 @@  parse_has_include (cpp_reader *pfile, enum include_type type)
 
   if (paren && !SEEN_EOL () && cpp_get_token (pfile)->type != CPP_CLOSE_PAREN)
     cpp_error (pfile, CPP_DL_ERROR,
-	       "missing ')' after \"__has_include__\"");
+	       "missing ')' after \"%s\" operand", NODE_NAME (op));
 
-  /* A possible controlling macro of the form #if !__has_include__ ().
-     _cpp_parse_expr checks there was no other junk on the line.  */
   if (node)
     pfile->mi_ind_cmacro = node;
 
-  pfile->state.in__has_include__--;
-
   return result;
 }
diff --git c/libcpp/files.c w/libcpp/files.c
index 7abae7ae6ec..260e787c329 100644
--- c/libcpp/files.c
+++ w/libcpp/files.c
@@ -504,7 +504,8 @@  _cpp_find_failed (_cpp_file *file)
 
 _cpp_file *
 _cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir,
-		bool fake, int angle_brackets, bool implicit_preinclude,
+		int angle_brackets,
+		bool fake, bool implicit_preinclude, bool has_include,
 		location_t loc)
 {
   struct cpp_file_hash_entry *entry;
@@ -592,7 +593,8 @@  _cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir,
 		return NULL;
 	      }
 
-	    open_file_failed (pfile, file, angle_brackets, loc);
+	    if (!has_include)
+	      open_file_failed (pfile, file, angle_brackets, loc);
 	    break;
 	  }
 
@@ -1046,8 +1048,8 @@  _cpp_stack_include (cpp_reader *pfile, const char *fname, int angle_brackets,
   if (!dir)
     return false;
 
-  _cpp_file *file = _cpp_find_file (pfile, fname, dir, false, angle_brackets,
-				    type == IT_DEFAULT, loc);
+  _cpp_file *file = _cpp_find_file (pfile, fname, dir, angle_brackets,
+				    false, type == IT_DEFAULT, false, loc);
   if (type == IT_DEFAULT && file == NULL)
     return false;
 
@@ -1062,9 +1064,6 @@  open_file_failed (cpp_reader *pfile, _cpp_file *file, int angle_brackets,
   int sysp = pfile->line_table->highest_line > 1 && pfile->buffer ? pfile->buffer->sysp : 0;
   bool print_dep = CPP_OPTION (pfile, deps.style) > (angle_brackets || !!sysp);
 
-  if (pfile->state.in__has_include__)
-    return;
-
   errno = file->err_no;
   if (print_dep && CPP_OPTION (pfile, deps.missing_files) && errno == ENOENT)
     {
@@ -1343,7 +1342,8 @@  cpp_clear_file_cache (cpp_reader *pfile)
 void
 _cpp_fake_include (cpp_reader *pfile, const char *fname)
 {
-  _cpp_find_file (pfile, fname, pfile->buffer->file->dir, true, 0, false, 0);
+  _cpp_find_file (pfile, fname, pfile->buffer->file->dir,
+		  0, true, false, false, 0);
 }
 
 /* Not everyone who wants to set system-header-ness on a buffer can
@@ -1461,7 +1461,8 @@  _cpp_compare_file_date (cpp_reader *pfile, const char *fname,
   if (!dir)
     return -1;
 
-  file = _cpp_find_file (pfile, fname, dir, false, angle_brackets, false, 0);
+  file = _cpp_find_file (pfile, fname, dir, angle_brackets,
+			 false, false, false, 0);
   if (file->err_no)
     return -1;
 
@@ -2004,9 +2005,11 @@  _cpp_has_header (cpp_reader *pfile, const char *fname, int angle_brackets,
 		 enum include_type type)
 {
   cpp_dir *start_dir = search_path_head (pfile, fname, angle_brackets, type);
-  _cpp_file *file = _cpp_find_file (pfile, fname, start_dir,
-				    /*fake=*/false, angle_brackets,
-				    /*implicit_preinclude=*/false, 0);
+  _cpp_file *file = _cpp_find_file (pfile, fname, start_dir, angle_brackets,
+				    /*fake=*/false,
+				    /*implicit_preinclude=*/false,
+				    /*has_include=*/true,
+				    0);
   return file->err_no != ENOENT;
 }
 
diff --git c/libcpp/identifiers.c w/libcpp/identifiers.c
index 562d8fee3b5..9627e1bf4b0 100644
--- c/libcpp/identifiers.c
+++ w/libcpp/identifiers.c
@@ -72,8 +72,7 @@  _cpp_init_hashtable (cpp_reader *pfile, cpp_hash_table *table)
   s->n__VA_ARGS__->flags |= NODE_DIAGNOSTIC;
   s->n__VA_OPT__        = cpp_lookup (pfile, DSC("__VA_OPT__"));
   s->n__VA_OPT__->flags |= NODE_DIAGNOSTIC;
-  s->n__has_include__   = cpp_lookup (pfile, DSC("__has_include__"));
-  s->n__has_include_next__ = cpp_lookup (pfile, DSC("__has_include_next__"));
+  /* __has_include{,_next} are inited in cpp_init_builtins.  */
 }
 
 /* Tear down the identifier hash table.  */
diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
index 1c26c365347..56cbbd82750 100644
--- c/libcpp/include/cpplib.h
+++ w/libcpp/include/cpplib.h
@@ -859,7 +859,7 @@  enum cpp_builtin_type
   BT_PRAGMA,			/* `_Pragma' operator */
   BT_TIMESTAMP,			/* `__TIMESTAMP__' */
   BT_COUNTER,			/* `__COUNTER__' */
-  BT_HAS_ATTRIBUTE,		/* `__has_attribute__(x)' */
+  BT_HAS_ATTRIBUTE,		/* `__has_attribute(x)' */
   BT_HAS_BUILTIN		/* `__has_builtin(x)' */
 };
 
diff --git c/libcpp/init.c w/libcpp/init.c
index 2b4923e1451..e798140ef8b 100644
--- c/libcpp/init.c
+++ w/libcpp/init.c
@@ -578,6 +578,17 @@  cpp_init_builtins (cpp_reader *pfile, int hosted)
 
   if (CPP_OPTION (pfile, objc))
     _cpp_define_builtin (pfile, "__OBJC__ 1");
+
+  /* These two behave as macros for #ifdef, but are evaluated
+     specially inside #if.  */
+  _cpp_define_builtin (pfile, "__has_include __has_include");
+  _cpp_define_builtin (pfile, "__has_include_next __has_include_next");
+  pfile->spec_nodes.n__has_include
+    = cpp_lookup (pfile, DSC("__has_include"));
+  pfile->spec_nodes.n__has_include->flags |= NODE_DIAGNOSTIC;
+  pfile->spec_nodes.n__has_include_next
+    = cpp_lookup (pfile, DSC("__has_include_next"));
+  pfile->spec_nodes.n__has_include_next->flags |= NODE_DIAGNOSTIC;
 }
 
 /* Sanity-checks are dependent on command-line options, so it is
@@ -672,7 +683,8 @@  cpp_read_main_file (cpp_reader *pfile, const char *fname)
     }
 
   pfile->main_file
-    = _cpp_find_file (pfile, fname, &pfile->no_search_path, false, 0, false,
+    = _cpp_find_file (pfile, fname, &pfile->no_search_path, /*angle=*/0,
+		      /*fake=*/false, /*preinclude=*/false, /*hasinclude=*/false,
 		      loc);
   if (_cpp_find_failed (pfile->main_file))
     return NULL;
diff --git c/libcpp/internal.h w/libcpp/internal.h
index 3623baf8191..5453c3bff85 100644
--- c/libcpp/internal.h
+++ w/libcpp/internal.h
@@ -268,9 +268,6 @@  struct lexer_state
   /* Nonzero when parsing arguments to a function-like macro.  */
   unsigned char parsing_args;
 
-  /* Nonzero if in a __has_include__ or __has_include_next__ statement.  */
-  unsigned char in__has_include__;
-
   /* Nonzero if prevent_expansion is true only because output is
      being discarded.  */
   unsigned char discarding_output;
@@ -293,8 +290,8 @@  struct spec_nodes
   cpp_hashnode *n_false;		/* C++ keyword false */
   cpp_hashnode *n__VA_ARGS__;		/* C99 vararg macros */
   cpp_hashnode *n__VA_OPT__;		/* C++ vararg macros */
-  cpp_hashnode *n__has_include__;	/* __has_include__ operator */
-  cpp_hashnode *n__has_include_next__;	/* __has_include_next__ operator */
+  cpp_hashnode *n__has_include;		/* __has_include operator */
+  cpp_hashnode *n__has_include_next;	/* __has_include_next operator */
 };
 
 typedef struct _cpp_line_note _cpp_line_note;
@@ -641,6 +638,16 @@  cpp_in_primary_file (cpp_reader *pfile)
   return pfile->line_table->depth == 1;
 }
 
+/* True if NODE is a macro for the purposes of ifdef, defined etc.  */
+inline bool _cpp_defined_macro_p (cpp_hashnode *node)
+{
+  /* Do not treat conditional macros as being defined.  This is due to
+     the powerpc port using conditional macros for 'vector', 'bool',
+     and 'pixel' to act as conditional keywords.  This messes up tests
+     like #ifndef bool.  */
+  return cpp_macro_p (node) && !(node->flags & NODE_CONDITIONAL);
+}
+
 /* In macro.c */
 extern void _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node);
 inline void _cpp_maybe_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node)
@@ -674,7 +681,8 @@  extern void _cpp_destroy_hashtable (cpp_reader *);
 /* In files.c */
 typedef struct _cpp_file _cpp_file;
 extern _cpp_file *_cpp_find_file (cpp_reader *, const char *, cpp_dir *,
-				  bool, int, bool, location_t);
+				  int angle, bool fake, bool preinclude,
+				  bool has_include, location_t);
 extern bool _cpp_find_failed (_cpp_file *);
 extern void _cpp_mark_file_once_only (cpp_reader *, struct _cpp_file *);
 extern void _cpp_fake_include (cpp_reader *, const char *);
diff --git c/libcpp/pch.c w/libcpp/pch.c
index 607f805bebe..e631050936b 100644
--- c/libcpp/pch.c
+++ w/libcpp/pch.c
@@ -811,8 +811,8 @@  cpp_read_state (cpp_reader *r, const char *name, FILE *f,
     s->n_false		= cpp_lookup (r, DSC("false"));
     s->n__VA_ARGS__     = cpp_lookup (r, DSC("__VA_ARGS__"));
     s->n__VA_OPT__      = cpp_lookup (r, DSC("__VA_OPT__"));
-    s->n__has_include__ = cpp_lookup (r, DSC("__has_include__"));
-    s->n__has_include_next__ = cpp_lookup (r, DSC("__has_include_next__"));
+    s->n__has_include   = cpp_lookup (r, DSC("__has_include"));
+    s->n__has_include_next = cpp_lookup (r, DSC("__has_include_next"));
   }
 
   old_state = r->state;
diff --git c/libcpp/traditional.c w/libcpp/traditional.c
index 21c63b47dd5..ff06d31a897 100644
--- c/libcpp/traditional.c
+++ w/libcpp/traditional.c
@@ -78,8 +78,8 @@  enum ls {ls_none = 0,		/* Normal state.  */
 	 ls_hash,		/* After # in preprocessor conditional.  */
 	 ls_predicate,		/* After the predicate, maybe paren?  */
 	 ls_answer,		/* In answer to predicate.  */
-	 ls_has_include,	/* After __has_include__.  */
-	 ls_has_include_close};	/* Looking for ')' of __has_include__.  */
+	 ls_has_include,	/* After __has_include.  */
+	 ls_has_include_close};	/* Looking for ')' of __has_include.  */
 
 /* Lexing TODO: Maybe handle space in escaped newlines.  Stop lex.c
    from recognizing comments and directives during its lexing pass.  */
@@ -565,8 +565,8 @@  _cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro,
 		  continue;
 		}
 	      else if (pfile->state.in_expression
-		       && (node == pfile->spec_nodes.n__has_include__
-			|| node == pfile->spec_nodes.n__has_include_next__))
+		       && (node == pfile->spec_nodes.n__has_include
+			|| node == pfile->spec_nodes.n__has_include_next))
 		{
 		  lex_state = ls_has_include;
 		  continue;