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
2014-09-02 Edward Smith-Rowland <3dw...@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.
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 ')'. */