aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2021-05-11 23:54:01 +0000
committerJoseph Myers <joseph@codesourcery.com>2021-05-11 23:54:01 +0000
commit71d38ec80008afdbb9a059253407d80598b765c0 (patch)
tree61d4686bb430d8369941427a4d05c34522007f48
parent415bcabf134b90794a134ca5dadfaf44496645d5 (diff)
downloadgcc-71d38ec80008afdbb9a059253407d80598b765c0.zip
gcc-71d38ec80008afdbb9a059253407d80598b765c0.tar.gz
gcc-71d38ec80008afdbb9a059253407d80598b765c0.tar.bz2
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.
-rw-r--r--gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c16
-rw-r--r--gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c57
-rw-r--r--gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c63
-rw-r--r--libcpp/directives.c68
-rw-r--r--libcpp/include/cpplib.h3
-rw-r--r--libcpp/init.c52
6 files changed, 224 insertions, 35 deletions
diff --git a/gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c b/gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c
new file mode 100644
index 0000000..2d5809a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c
@@ -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
diff --git a/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c
new file mode 100644
index 0000000..b23e311
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c
@@ -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
diff --git a/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c
new file mode 100644
index 0000000..9132832
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c
@@ -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
diff --git a/libcpp/directives.c b/libcpp/directives.c
index 795f93e..261a584 100644
--- a/libcpp/directives.c
+++ b/libcpp/directives.c
@@ -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)
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 41d75d9..2cdaf19 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -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;
diff --git a/libcpp/init.c b/libcpp/init.c
index 18a2341..d5d4e99 100644
--- a/libcpp/init.c
+++ b/libcpp/init.c
@@ -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. */