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:
Joseph Myers 2021-05-11 23:54:01 +00:00
parent 415bcabf13
commit 71d38ec800
6 changed files with 224 additions and 35 deletions

View 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

View 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

View 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

View File

@ -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)

View File

@ -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;

View File

@ -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. */