preprocessor: Support C2X #elifdef, #elifndef
C2X adds #elifdef and #elifndef preprocessor directives; these have also been proposed for C++. Implement these directives in libcpp accordingly. In this implementation, #elifdef and #elifndef are treated as non-directives for any language version other than c2x and gnu2x (if the feature is accepted for C++, it can trivially be enabled for relevant C++ versions). In strict conformance modes for prior language versions, this is required, as illustrated by the c11-elifdef-1.c test added. Bootstrapped with no regressions for x86_64-pc-linux-gnu. libcpp/ * include/cpplib.h (struct cpp_options): Add elifdef. * init.c (struct lang_flags): Add elifdef. (lang_defaults): Update to include elifdef initializers. (cpp_set_lang): Set elifdef for pfile based on language. * directives.c (STDC2X, ELIFDEF): New macros. (EXTENSION): Increase value to 3. (DIRECTIVE_TABLE): Add #elifdef and #elifndef. (_cpp_handle_directive): Do not treat ELIFDEF directives as directives for language versions without the #elifdef feature. (do_elif): Handle #elifdef and #elifndef. (do_elifdef, do_elifndef): New functions. gcc/testsuite/ * gcc.dg/cpp/c11-elifdef-1.c, gcc.dg/cpp/c2x-elifdef-1.c, gcc.dg/cpp/c2x-elifdef-2.c: New tests.
This commit is contained in:
parent
415bcabf13
commit
71d38ec800
6 changed files with 224 additions and 35 deletions
16
gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c
Normal file
16
gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* Test #elifdef and #elifndef not in C11. */
|
||||
/* { dg-do preprocess } */
|
||||
/* { dg-options "-std=c11 -pedantic-errors" } */
|
||||
|
||||
#define A
|
||||
#undef B
|
||||
|
||||
#if 0
|
||||
#elifdef A
|
||||
#error "#elifdef A applied"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifndef B
|
||||
#error "#elifndef B applied"
|
||||
#endif
|
57
gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c
Normal file
57
gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* Test #elifdef and #elifndef in C2x. */
|
||||
/* { dg-do preprocess } */
|
||||
/* { dg-options "-std=c2x -pedantic-errors" } */
|
||||
|
||||
#define A
|
||||
#undef B
|
||||
|
||||
#if 0
|
||||
#elifdef A
|
||||
#define M1 1
|
||||
#endif
|
||||
|
||||
#if M1 != 1
|
||||
#error "#elifdef A did not apply"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifdef B
|
||||
#error "#elifdef B applied"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifndef A
|
||||
#error "#elifndef A applied"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifndef B
|
||||
#define M2 2
|
||||
#endif
|
||||
|
||||
#if M2 != 2
|
||||
#error "#elifndef B did not apply"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifdef A
|
||||
#else
|
||||
#error "#elifdef A did not apply"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifndef B
|
||||
#else
|
||||
#error "#elifndef B did not apply"
|
||||
#endif
|
||||
|
||||
/* As with #elif, the syntax of the new directives is relaxed after a
|
||||
non-skipped group. */
|
||||
|
||||
#if 1
|
||||
#elifdef x * y
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#elifndef !
|
||||
#endif
|
63
gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c
Normal file
63
gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* Test #elifdef and #elifndef in C2x: erroneous usages. */
|
||||
/* { dg-do preprocess } */
|
||||
/* { dg-options "-std=c2x -pedantic-errors" } */
|
||||
|
||||
#define A
|
||||
#undef B
|
||||
|
||||
#elifdef A /* { dg-error "#elifdef without #if" } */
|
||||
#elifdef B /* { dg-error "#elifdef without #if" } */
|
||||
#elifndef A /* { dg-error "#elifndef without #if" } */
|
||||
#elifndef B /* { dg-error "#elifndef without #if" } */
|
||||
|
||||
#if 1 /* { dg-error "-:began here" } */
|
||||
#else
|
||||
#elifdef A /* { dg-error "#elifdef after #else" } */
|
||||
#endif
|
||||
|
||||
#if 1 /* { dg-error "-:began here" } */
|
||||
#else
|
||||
#elifdef B /* { dg-error "#elifdef after #else" } */
|
||||
#endif
|
||||
|
||||
#if 1 /* { dg-error "-:began here" } */
|
||||
#else
|
||||
#elifndef A /* { dg-error "#elifndef after #else" } */
|
||||
#endif
|
||||
|
||||
#if 1 /* { dg-error "-:began here" } */
|
||||
#else
|
||||
#elifndef B /* { dg-error "#elifndef after #else" } */
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifdef A = /* { dg-error "extra tokens at end of #elifdef directive" } */
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifdef B = /* { dg-error "extra tokens at end of #elifdef directive" } */
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifndef A = /* { dg-error "extra tokens at end of #elifndef directive" } */
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifndef B = /* { dg-error "extra tokens at end of #elifndef directive" } */
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifdef /* { dg-error "no macro name given in #elifdef directive" } */
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifndef /* { dg-error "no macro name given in #elifndef directive" } */
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifdef , /* { dg-error "macro names must be identifiers" } */
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elifndef , /* { dg-error "macro names must be identifiers" } */
|
||||
#endif
|
|
@ -56,10 +56,12 @@ struct pragma_entry
|
|||
|
||||
/* Values for the origin field of struct directive. KANDR directives
|
||||
come from traditional (K&R) C. STDC89 directives come from the
|
||||
1989 C standard. EXTENSION directives are extensions. */
|
||||
1989 C standard. STDC2X directives come from the C2X standard. EXTENSION
|
||||
directives are extensions. */
|
||||
#define KANDR 0
|
||||
#define STDC89 1
|
||||
#define EXTENSION 2
|
||||
#define STDC2X 2
|
||||
#define EXTENSION 3
|
||||
|
||||
/* Values for the flags field of struct directive. COND indicates a
|
||||
conditional; IF_COND an opening conditional. INCL means to treat
|
||||
|
@ -67,13 +69,17 @@ struct pragma_entry
|
|||
means this directive should be handled even if -fpreprocessed is in
|
||||
effect (these are the directives with callback hooks).
|
||||
|
||||
EXPAND is set on directives that are always macro-expanded. */
|
||||
EXPAND is set on directives that are always macro-expanded.
|
||||
|
||||
ELIFDEF is set on directives that are only handled for standards with the
|
||||
#elifdef / #elifndef feature. */
|
||||
#define COND (1 << 0)
|
||||
#define IF_COND (1 << 1)
|
||||
#define INCL (1 << 2)
|
||||
#define IN_I (1 << 3)
|
||||
#define EXPAND (1 << 4)
|
||||
#define DEPRECATED (1 << 5)
|
||||
#define ELIFDEF (1 << 6)
|
||||
|
||||
/* Defines one #-directive, including how to handle it. */
|
||||
typedef void (*directive_handler) (cpp_reader *);
|
||||
|
@ -148,6 +154,8 @@ static void cpp_pop_definition (cpp_reader *, struct def_pragma_macro *);
|
|||
D(undef, T_UNDEF, KANDR, IN_I) \
|
||||
D(line, T_LINE, KANDR, EXPAND) \
|
||||
D(elif, T_ELIF, STDC89, COND | EXPAND) \
|
||||
D(elifdef, T_ELIFDEF, STDC2X, COND | ELIFDEF) \
|
||||
D(elifndef, T_ELIFNDEF, STDC2X, COND | ELIFDEF) \
|
||||
D(error, T_ERROR, STDC89, 0) \
|
||||
D(pragma, T_PRAGMA, STDC89, IN_I) \
|
||||
D(warning, T_WARNING, EXTENSION, 0) \
|
||||
|
@ -437,7 +445,11 @@ _cpp_handle_directive (cpp_reader *pfile, bool indented)
|
|||
if (dname->type == CPP_NAME)
|
||||
{
|
||||
if (dname->val.node.node->is_directive)
|
||||
dir = &dtable[dname->val.node.node->directive_index];
|
||||
{
|
||||
dir = &dtable[dname->val.node.node->directive_index];
|
||||
if ((dir->flags & ELIFDEF) && !CPP_OPTION (pfile, elifdef))
|
||||
dir = 0;
|
||||
}
|
||||
}
|
||||
/* We do not recognize the # followed by a number extension in
|
||||
assembler code. */
|
||||
|
@ -2079,8 +2091,8 @@ do_else (cpp_reader *pfile)
|
|||
}
|
||||
}
|
||||
|
||||
/* Handle a #elif directive by not changing if_stack either. See the
|
||||
comment above do_else. */
|
||||
/* Handle a #elif, #elifdef or #elifndef directive by not changing if_stack
|
||||
either. See the comment above do_else. */
|
||||
static void
|
||||
do_elif (cpp_reader *pfile)
|
||||
{
|
||||
|
@ -2088,12 +2100,13 @@ do_elif (cpp_reader *pfile)
|
|||
struct if_stack *ifs = buffer->if_stack;
|
||||
|
||||
if (ifs == NULL)
|
||||
cpp_error (pfile, CPP_DL_ERROR, "#elif without #if");
|
||||
cpp_error (pfile, CPP_DL_ERROR, "#%s without #if", pfile->directive->name);
|
||||
else
|
||||
{
|
||||
if (ifs->type == T_ELSE)
|
||||
{
|
||||
cpp_error (pfile, CPP_DL_ERROR, "#elif after #else");
|
||||
cpp_error (pfile, CPP_DL_ERROR, "#%s after #else",
|
||||
pfile->directive->name);
|
||||
cpp_error_with_line (pfile, CPP_DL_ERROR, ifs->line, 0,
|
||||
"the conditional began here");
|
||||
}
|
||||
|
@ -2107,8 +2120,29 @@ do_elif (cpp_reader *pfile)
|
|||
pfile->state.skipping = 1;
|
||||
else
|
||||
{
|
||||
pfile->state.skipping = ! _cpp_parse_expr (pfile, false);
|
||||
ifs->skip_elses = ! pfile->state.skipping;
|
||||
if (pfile->directive == &dtable[T_ELIF])
|
||||
pfile->state.skipping = !_cpp_parse_expr (pfile, false);
|
||||
else
|
||||
{
|
||||
cpp_hashnode *node = lex_macro_node (pfile, false);
|
||||
|
||||
if (node)
|
||||
{
|
||||
bool macro_defined = _cpp_defined_macro_p (node);
|
||||
if (!_cpp_maybe_notify_macro_use (pfile, node,
|
||||
pfile->directive_line))
|
||||
/* It wasn't a macro after all. */
|
||||
macro_defined = false;
|
||||
bool skip = (pfile->directive == &dtable[T_ELIFDEF]
|
||||
? !macro_defined
|
||||
: macro_defined);
|
||||
if (pfile->cb.used)
|
||||
pfile->cb.used (pfile, pfile->directive_line, node);
|
||||
check_eol (pfile, false);
|
||||
pfile->state.skipping = skip;
|
||||
}
|
||||
}
|
||||
ifs->skip_elses = !pfile->state.skipping;
|
||||
}
|
||||
|
||||
/* Invalidate any controlling macro. */
|
||||
|
@ -2116,6 +2150,20 @@ do_elif (cpp_reader *pfile)
|
|||
}
|
||||
}
|
||||
|
||||
/* Handle a #elifdef directive. */
|
||||
static void
|
||||
do_elifdef (cpp_reader *pfile)
|
||||
{
|
||||
do_elif (pfile);
|
||||
}
|
||||
|
||||
/* Handle a #elifndef directive. */
|
||||
static void
|
||||
do_elifndef (cpp_reader *pfile)
|
||||
{
|
||||
do_elif (pfile);
|
||||
}
|
||||
|
||||
/* #endif pops the if stack and resets pfile->state.skipping. */
|
||||
static void
|
||||
do_endif (cpp_reader *pfile)
|
||||
|
|
|
@ -497,6 +497,9 @@ struct cpp_options
|
|||
/* Nonzero for the '::' token. */
|
||||
unsigned char scope;
|
||||
|
||||
/* Nonzero for the '#elifdef' and '#elifndef' directives. */
|
||||
unsigned char elifdef;
|
||||
|
||||
/* Nonzero means tokenize C++20 module directives. */
|
||||
unsigned char module_directives;
|
||||
|
||||
|
|
|
@ -95,34 +95,35 @@ struct lang_flags
|
|||
char scope;
|
||||
char dfp_constants;
|
||||
char size_t_literals;
|
||||
char elifdef;
|
||||
};
|
||||
|
||||
static const struct lang_flags lang_defaults[] =
|
||||
{ /* c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit vaopt scope dfp szlit */
|
||||
/* GNUC89 */ { 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 },
|
||||
/* GNUC99 */ { 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 },
|
||||
/* GNUC11 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 },
|
||||
/* GNUC17 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 },
|
||||
/* GNUC2X */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0 },
|
||||
/* STDC89 */ { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
|
||||
/* STDC94 */ { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
|
||||
/* STDC99 */ { 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
|
||||
/* STDC11 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
|
||||
/* STDC17 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
|
||||
/* STDC2X */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0 },
|
||||
/* GNUCXX */ { 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 },
|
||||
/* CXX98 */ { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 },
|
||||
/* GNUCXX11 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0 },
|
||||
/* CXX11 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
|
||||
/* GNUCXX14 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0 },
|
||||
/* CXX14 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0 },
|
||||
/* GNUCXX17 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 },
|
||||
/* CXX17 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0 },
|
||||
/* GNUCXX20 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 },
|
||||
/* CXX20 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 },
|
||||
/* GNUCXX23 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 },
|
||||
/* CXX23 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 },
|
||||
/* ASM */ { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
{ /* c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit vaopt scope dfp szlit elifdef */
|
||||
/* GNUC89 */ { 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
|
||||
/* GNUC99 */ { 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
|
||||
/* GNUC11 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
|
||||
/* GNUC17 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
|
||||
/* GNUC2X */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1 },
|
||||
/* STDC89 */ { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
|
||||
/* STDC94 */ { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
|
||||
/* STDC99 */ { 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
|
||||
/* STDC11 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
|
||||
/* STDC17 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
|
||||
/* STDC2X */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1 },
|
||||
/* GNUCXX */ { 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
|
||||
/* CXX98 */ { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0 },
|
||||
/* GNUCXX11 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
|
||||
/* CXX11 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 },
|
||||
/* GNUCXX14 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
|
||||
/* CXX14 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 },
|
||||
/* GNUCXX17 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0 },
|
||||
/* CXX17 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0 },
|
||||
/* GNUCXX20 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0 },
|
||||
/* CXX20 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0 },
|
||||
/* GNUCXX23 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0 },
|
||||
/* CXX23 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0 },
|
||||
/* ASM */ { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/* Sets internal flags correctly for a given language. */
|
||||
|
@ -151,6 +152,7 @@ cpp_set_lang (cpp_reader *pfile, enum c_lang lang)
|
|||
CPP_OPTION (pfile, scope) = l->scope;
|
||||
CPP_OPTION (pfile, dfp_constants) = l->dfp_constants;
|
||||
CPP_OPTION (pfile, size_t_literals) = l->size_t_literals;
|
||||
CPP_OPTION (pfile, elifdef) = l->elifdef;
|
||||
}
|
||||
|
||||
/* Initialize library global state. */
|
||||
|
|
Loading…
Add table
Reference in a new issue