[PATCH C++] - SD-6 Implementation Part 1 - __has_include.

Ed Smith-Rowland 3dw4rd@verizon.net
Tue Sep 2 01:34:00 GMT 2014


Greetings,

I am finally getting back to my SD-6 C++ features test work.
This first part adds a __has_include__ built-in that will return true if 
a header exists.
I also added __has_include_next__ as an extension.  Clang has this 
extension.

Both these built-ins will be wrapped in function type macros in a later 
patch to c-family.

As written, these are available to the whole C-family rather than just C++.
I think this makes a valuable addition for everyone.
(I sort of wonder why this wasn't added to the actual preprocessor 20 
years ago.)

Bootstrapped and tested under x86_64-linux.

OK?

Ed

-------------- next part --------------
2014-09-02  Edward Smith-Rowland  <3dw4rd@verizon.net>

	Implement SD-6: SG10 Feature Test Recommendations
	* internal.h (lexer_state, spec_nodes): Add in__has_include__.
	* directives.c: Support __has_include__ builtin.
	* expr.c (parse_has_include): New function to parse __has_include__
	builtin; (eval_token()): Use it.
	* files.c (_cpp_has_header()): New funtion to look for header;
	(open_file_failed()): Not an error to not find a header file for
	__has_include__.
	* identifiers.c (_cpp_init_hashtable()): Add entry for __has_include__.
	* pch.c (cpp_read_state): Lookup __has_include__.
	* traditional.c (enum ls, _cpp_scan_out_logical_line()): Walk through
	__has_include__ statements.

-------------- next part --------------
Index: internal.h
===================================================================
--- internal.h	(revision 214680)
+++ internal.h	(working copy)
@@ -258,6 +258,9 @@
   /* Nonzero when parsing arguments to a function-like macro.  */
   unsigned char parsing_args;
 
+  /* Nonzero to prevent macro expansion.  */
+  unsigned char in__has_include__;
+
   /* Nonzero if prevent_expansion is true only because output is
      being discarded.  */
   unsigned char discarding_output;
@@ -279,6 +282,8 @@
   cpp_hashnode *n_true;			/* C++ keyword true */
   cpp_hashnode *n_false;		/* C++ keyword false */
   cpp_hashnode *n__VA_ARGS__;		/* C99 vararg macros */
+  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;
@@ -645,6 +650,8 @@
 extern bool _cpp_read_file_entries (cpp_reader *, FILE *);
 extern const char *_cpp_get_file_name (_cpp_file *);
 extern struct stat *_cpp_get_file_stat (_cpp_file *);
+extern bool _cpp_has_header (cpp_reader *, const char *, int,
+			     enum include_type);
 
 /* In expr.c */
 extern bool _cpp_parse_expr (cpp_reader *, bool);
@@ -680,6 +687,7 @@
 extern void _cpp_do_file_change (cpp_reader *, enum lc_reason, const char *,
 				 linenum_type, unsigned int);
 extern void _cpp_pop_buffer (cpp_reader *);
+extern char *_cpp_bracket_include (cpp_reader *);
 
 /* In directives.c */
 struct _cpp_dir_only_callbacks
Index: directives.c
===================================================================
--- directives.c	(revision 214680)
+++ directives.c	(working copy)
@@ -549,6 +549,11 @@
       if (is_def_or_undef && node == pfile->spec_nodes.n_defined)
 	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");
       else if (! (node->flags & NODE_POISONED))
 	return node;
     }
@@ -2601,3 +2606,12 @@
       node->directive_index = i;
     }
 }
+
+/* Extract header file from a bracket include. Parsing starts after '<'.
+   The string is malloced and must be freed by the caller.  */
+char *
+_cpp_bracket_include(cpp_reader *pfile)
+{
+  return glue_header_name (pfile);
+}
+
Index: expr.c
===================================================================
--- expr.c	(revision 214680)
+++ expr.c	(working copy)
@@ -64,6 +64,8 @@
 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);
+
 /* Token type abuse to create unary plus and minus operators.  */
 #define CPP_UPLUS ((enum cpp_ttype) (CPP_LAST_CPP_OP + 1))
 #define CPP_UMINUS ((enum cpp_ttype) (CPP_LAST_CPP_OP + 2))
@@ -1048,6 +1050,10 @@
     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 (CPP_OPTION (pfile, cplusplus)
 	       && (token->val.node.node == pfile->spec_nodes.n_true
 		   || token->val.node.node == pfile->spec_nodes.n_false))
@@ -2072,3 +2078,72 @@
 
   return lhs;
 }
+
+/* Handle meeting "__has_include__" in a preprocessor expression.  */
+static cpp_num
+parse_has_include (cpp_reader *pfile, enum include_type type)
+{
+  cpp_num result;
+  bool paren = false;
+  cpp_hashnode *node = 0;
+  const cpp_token *token;
+  bool bracket = false;
+  char *fname = 0;
+
+  result.unsignedp = false;
+  result.high = 0;
+  result.overflow = false;
+  result.low = 0;
+
+  pfile->state.in__has_include__++;
+
+  token = cpp_get_token (pfile);
+  if (token->type == CPP_OPEN_PAREN)
+    {
+      paren = true;
+      token = cpp_get_token (pfile);
+    }
+
+  if (token->type == CPP_STRING || token->type == CPP_HEADER_NAME)
+    {
+      if (token->type == CPP_HEADER_NAME)
+	bracket = true;
+      fname = XNEWVEC (char, token->val.str.len - 1);
+      memcpy (fname, token->val.str.text + 1, token->val.str.len - 2);
+      fname[token->val.str.len - 2] = '\0';
+      node = token->val.node.node;
+    }
+  else if (token->type == CPP_LESS)
+    {
+      bracket = true;
+      fname = _cpp_bracket_include (pfile);
+    }
+  else
+    cpp_error (pfile, CPP_DL_ERROR,
+	       "operator \"__has_include__\" requires a header string");
+
+  if (fname)
+    {
+      int angle_brackets = (bracket ? 1 : 0);
+
+      if (_cpp_has_header (pfile, fname, angle_brackets, type))
+	result.low = 0;
+      else
+	result.low = 1;
+
+      XDELETEVEC (fname);
+    }
+
+  if (paren && cpp_get_token (pfile)->type != CPP_CLOSE_PAREN)
+    cpp_error (pfile, CPP_DL_ERROR,
+	       "missing ')' after \"__has_include__\"");
+
+  /* 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;
+}
Index: files.c
===================================================================
--- files.c	(revision 214680)
+++ files.c	(working copy)
@@ -1029,6 +1029,9 @@
   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)
     {
@@ -1945,3 +1948,17 @@
   return bsearch (&d, pchf->entries, pchf->count, sizeof (struct pchf_entry),
 		  pchf_compare) != NULL;
 }
+
+/* Return true if the file FNAME is found in te appropriate include file path
+   as indicated by ANGLE_BRACKETS.  */
+
+bool
+_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);
+  return _cpp_find_failed (_cpp_find_file (pfile, fname, start_dir,
+			   /*fake=*/false, angle_brackets,
+			   /*implicit_preinclude=*/false));
+}
+
Index: identifiers.c
===================================================================
--- identifiers.c	(revision 214680)
+++ identifiers.c	(working copy)
@@ -72,6 +72,8 @@
   s->n_false		= cpp_lookup (pfile, DSC("false"));
   s->n__VA_ARGS__       = cpp_lookup (pfile, DSC("__VA_ARGS__"));
   s->n__VA_ARGS__->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__"));
 }
 
 /* Tear down the identifier hash table.  */
Index: pch.c
===================================================================
--- pch.c	(revision 214680)
+++ pch.c	(working copy)
@@ -833,6 +833,8 @@
     s->n_true		= cpp_lookup (r, DSC("true"));
     s->n_false		= cpp_lookup (r, DSC("false"));
     s->n__VA_ARGS__     = cpp_lookup (r, DSC("__VA_ARGS__"));
+    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;
Index: traditional.c
===================================================================
--- traditional.c	(revision 214680)
+++ traditional.c	(working copy)
@@ -74,7 +74,9 @@
 	 ls_defined_close,	/* Looking for ')' of defined().  */
 	 ls_hash,		/* After # in preprocessor conditional.  */
 	 ls_predicate,		/* After the predicate, maybe paren?  */
-	 ls_answer};		/* In answer to predicate.  */
+	 ls_answer,		/* In answer to predicate.  */
+	 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.  */
@@ -524,6 +526,13 @@
 		  lex_state = ls_defined;
 		  continue;
 		}
+	      else if (pfile->state.in_expression
+		       && (node == pfile->spec_nodes.n__has_include__
+			|| node == pfile->spec_nodes.n__has_include_next__))
+		{
+		  lex_state = ls_has_include;
+		  continue;
+		}
 	    }
 	  break;
 
@@ -547,6 +556,8 @@
 		lex_state = ls_answer;
 	      else if (lex_state == ls_defined)
 		lex_state = ls_defined_close;
+	      else if (lex_state == ls_has_include)
+		lex_state = ls_has_include_close;
 	    }
 	  break;
 
@@ -584,7 +595,8 @@
 		      goto new_context;
 		    }
 		}
-	      else if (lex_state == ls_answer || lex_state == ls_defined_close)
+	      else if (lex_state == ls_answer || lex_state == ls_defined_close
+			|| lex_state == ls_has_include_close)
 		lex_state = ls_none;
 	    }
 	  break;
@@ -665,7 +677,8 @@
 	lex_state = ls_none;
       else if (lex_state == ls_hash
 	       || lex_state == ls_predicate
-	       || lex_state == ls_defined)
+	       || lex_state == ls_defined
+	       || lex_state == ls_has_include)
 	lex_state = ls_none;
 
       /* ls_answer and ls_defined_close keep going until ')'.  */


More information about the Gcc-patches mailing list