This patch attempts to implement the C11 _Generic feature.
Based on the last comment in
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46073
I am not at all sure I've done it correctly.
There are a couple of other things that aren't clear to me.
First, should c_parser_generic_selection call mark_exp_read on each
expression in the generic association list? I chose not to, on the
basis that those expressions are parsed but not evaluated or otherwise
used; but I am not sure that this is correct. (I did choose to call
it for the controlling expression, by analogy with typeof).
Second, it isn't clear to me whether setting
c_inhibit_evaluation_warnings is sufficient here. I welcome your
advice.
Finally, I'd appreciate advice on the content of the various error
messages.
I wrote some new tests. I tried to test every constraint in the spec,
but I have never really been all that good at language lawyering.
Comments?
2012-07-27 Tom Tromey <[email protected]>
* c-common.h (enum rid) <RID_GENERIC>: New constant.
* c-common.c (c_common_reswords): Add _Generic.
2012-07-27 Tom Tromey <[email protected]>
* c-parser.c (struct c_generic_association): New.
(c_generic_association_d): New typedef.
(c_parser_generic_selection): New function.
(c_parser_postfix_expression): Handle RID_GENERIC.
2012-07-27 Tom Tromey <[email protected]>
* gcc.dg/c11-generic-2.c: New file.
* gcc.dg/c11-generic-1.c: New file.
---
gcc/c-family/ChangeLog | 5 +
gcc/c-family/c-common.c | 1 +
gcc/c-family/c-common.h | 4 +-
gcc/c/ChangeLog | 7 ++
gcc/c/c-parser.c | 193 ++++++++++++++++++++++++++++++++++
gcc/testsuite/ChangeLog | 5 +
gcc/testsuite/gcc.dg/c11-generic-1.c | 28 +++++
gcc/testsuite/gcc.dg/c11-generic-2.c | 24 ++++
8 files changed, 265 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/c11-generic-1.c
create mode 100644 gcc/testsuite/gcc.dg/c11-generic-2.c
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index b72506b..cc880de 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -418,6 +418,7 @@ const struct c_common_resword c_common_reswords[] =
{ "_Sat", RID_SAT, D_CONLY | D_EXT },
{ "_Static_assert", RID_STATIC_ASSERT, D_CONLY },
{ "_Noreturn", RID_NORETURN, D_CONLY },
+ { "_Generic", RID_GENERIC, D_CONLY },
{ "__FUNCTION__", RID_FUNCTION_NAME, 0 },
{ "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 },
{ "__alignof", RID_ALIGNOF, 0 },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 050112e..415b6e0 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1,6 +1,6 @@
/* Definitions for c-common.c.
Copyright (C) 1987, 1993, 1994, 1995, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011, 2012
Free Software Foundation, Inc.
This file is part of GCC.
@@ -108,7 +108,7 @@ enum rid
RID_FRACT, RID_ACCUM,
/* C11 */
- RID_ALIGNAS,
+ RID_ALIGNAS, RID_GENERIC,
/* This means to warn that this is a C++ keyword, and then treat it
as a normal identifier. */
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 2237749..360cc58 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -6158,6 +6158,195 @@ c_parser_get_builtin_args (c_parser *parser, const char
*bname,
return true;
}
+/* This represents a single generic-association. */
+
+struct c_generic_association
+{
+ /* The location of the starting token of the type. */
+ location_t type_location;
+ /* The association's type, or NULL_TREE for 'default'.. */
+ tree type;
+ /* The association's expression. */
+ struct c_expr expression;
+};
+
+typedef struct c_generic_association c_generic_association_d;
+
+DEF_VEC_O (c_generic_association_d);
+DEF_VEC_ALLOC_O (c_generic_association_d, heap);
+
+/* Parse a generic-selection. (C11 6.5.1.1).
+
+ generic-selection:
+ _Generic ( assignment-expression , generic-assoc-list )
+
+ generic-assoc-list:
+ generic-association
+ generic-assoc-list , generic-association
+
+ generic-association:
+ type-name : assignment-expression
+ default : assignment-expression
+*/
+
+static struct c_expr
+c_parser_generic_selection (c_parser *parser)
+{
+ VEC (c_generic_association_d, heap) *associations = NULL;
+ struct c_expr selector, error_expr;
+ tree selector_type;
+ struct c_generic_association matched_assoc;
+ int match_found = 0;
+ location_t generic_loc, selector_loc;
+
+ error_expr.original_code = ERROR_MARK;
+ error_expr.original_type = NULL;
+ error_expr.value = error_mark_node;
+
+ gcc_assert (c_parser_next_token_is_keyword (parser, RID_GENERIC));
+ generic_loc = c_parser_peek_token (parser)->location;
+ c_parser_consume_token (parser);
+ if (!flag_isoc11)
+ {
+ if (flag_isoc99)
+ pedwarn (generic_loc, OPT_Wpedantic,
+ "ISO C99 does not support %<_Generic%>");
+ else
+ pedwarn (generic_loc, OPT_Wpedantic,
+ "ISO C90 does not support %<_Generic%>");
+ }
+
+ if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<.%>"))
+ return error_expr;
+
+ c_inhibit_evaluation_warnings++;
+ selector_loc = c_parser_peek_token (parser)->location;
+ selector = c_parser_expr_no_commas (parser, NULL);
+ c_inhibit_evaluation_warnings--;
+
+ if (selector.value == error_mark_node)
+ return selector;
+ mark_exp_read (selector.value);
+ selector_type = TREE_TYPE (selector.value);
+
+ if (!c_parser_require (parser, CPP_COMMA, "expected %<.%>"))
+ return error_expr;
+
+ while (1)
+ {
+ struct c_generic_association assoc, *iter;
+ int ix;
+ c_token *token = c_parser_peek_token (parser);
+
+ assoc.type_location = token->location;
+ if (token->type == CPP_KEYWORD && token->keyword == RID_DEFAULT)
+ {
+ c_parser_consume_token (parser);
+ assoc.type = NULL_TREE;
+ }
+ else
+ {
+ struct c_type_name *type_name;
+
+ type_name = c_parser_type_name (parser);
+ if (type_name == NULL)
+ goto error_exit;
+ assoc.type = groktypename (type_name, NULL, NULL);
+ if (assoc.type == error_mark_node)
+ goto error_exit;
+
+ if (! COMPLETE_TYPE_P (assoc.type))
+ error_at (assoc.type_location,
+ "%<_Generic%> association has incomplete type");
+
+ if (variably_modified_type_p (assoc.type, NULL_TREE))
+ error_at (assoc.type_location,
+ "%<_Generic%> association has "
+ "variable length type");
+ }
+
+ if (!c_parser_require (parser, CPP_COLON, "expected %<.%>"))
+ goto error_exit;
+
+ assoc.expression = c_parser_expr_no_commas (parser, NULL);
+ if (assoc.expression.value == error_mark_node)
+ goto error_exit;
+
+ for (ix = 0;
+ VEC_iterate (c_generic_association_d, associations, ix, iter);
+ ++ix)
+ {
+ if (assoc.type == NULL_TREE)
+ {
+ if (iter->type == NULL_TREE)
+ {
+ error_at (assoc.type_location,
+ "duplicate %<default%> case in %<_Generic%>");
+ inform (iter->type_location, "original %<default%> is here");
+ }
+ }
+ else if (iter->type != NULL_TREE)
+ {
+ if (comptypes (assoc.type, iter->type))
+ {
+ error_at (assoc.type_location,
+ "%<_Generic%> specifies two compatible types");
+ inform (iter->type_location, "compatible type is here");
+ }
+ }
+ }
+
+ if (assoc.type == NULL_TREE)
+ {
+ if (!match_found)
+ {
+ matched_assoc = assoc;
+ match_found = 1;
+ }
+ }
+ else if (comptypes (assoc.type, selector_type))
+ {
+ if (!match_found || matched_assoc.type == NULL_TREE)
+ {
+ matched_assoc = assoc;
+ match_found = 1;
+ }
+ else
+ {
+ error_at (assoc.type_location,
+ "%<_Generic> selector matches multiple associations");
+ inform (matched_assoc.type_location,
+ "other match is here");
+ }
+ }
+
+ VEC_safe_push (c_generic_association_d, heap, associations, &assoc);
+
+ if (c_parser_peek_token (parser)->type != CPP_COMMA)
+ break;
+ c_parser_consume_token (parser);
+ }
+
+ VEC_free (c_generic_association_d, heap, associations);
+
+ if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<.%>"))
+ return error_expr;
+
+ if (!match_found)
+ {
+ error_at (selector_loc, "%<_Generic%> selector of type %qT is not "
+ "compatible with any association",
+ selector_type);
+ return error_expr;
+ }
+
+ mark_exp_read (matched_assoc.expression.value);
+ return matched_assoc.expression;
+
+ error_exit:
+ VEC_free (c_generic_association_d, heap, associations);
+ return error_expr;
+}
/* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2).
@@ -6181,6 +6370,7 @@ c_parser_get_builtin_args (c_parser *parser, const char
*bname,
constant
string-literal
( expression )
+ generic-selection
GNU extensions:
@@ -6749,6 +6939,9 @@ c_parser_postfix_expression (c_parser *parser)
expr.value = objc_build_encode_expr (type);
}
break;
+ case RID_GENERIC:
+ expr = c_parser_generic_selection (parser);
+ break;
default:
c_parser_error (parser, "expected expression");
expr.value = error_mark_node;
diff --git a/gcc/testsuite/gcc.dg/c11-generic-1.c
b/gcc/testsuite/gcc.dg/c11-generic-1.c
new file mode 100644
index 0000000..156d3a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-generic-1.c
@@ -0,0 +1,28 @@
+/* Test C11 _Generic. Valid uses. */
+/* { dg-do run } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+extern void exit (int);
+extern void abort (void);
+
+void
+check (int n)
+{
+ if (n)
+ abort ();
+}
+
+int
+main (void)
+{
+ int n = 0;
+
+ check (_Generic (n++, int: 0));
+ /* _Generic should not evaluate its argument. */
+ check (n);
+
+ check (_Generic (n, double: n++, default: 0));
+ check (n);
+
+ exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/c11-generic-2.c
b/gcc/testsuite/gcc.dg/c11-generic-2.c
new file mode 100644
index 0000000..db06a8e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-generic-2.c
@@ -0,0 +1,24 @@
+/* Test C11 _Generic. Error cases. */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+struct incomplete;
+
+void
+f (int n)
+{
+ /* Multiple 'default's. */
+ _Generic (n, default: 1, default: 2); /* { dg-error "duplicate .*default.*
case" } */
+
+ /* Variably-modified type not ok. */
+ _Generic (n, int[n]: 0, default: 1); /* { dg-error "variable length type" }
*/
+ /* Type must be complete. */
+ _Generic (n, struct incomplete: 0, default: 1); /* { dg-error "incomplete
type" } */
+ _Generic (n, void: 0, default: 1); /* { dg-error "incomplete type" } */
+
+ /* Two compatible types in association list. */
+ _Generic (&n, int: 5, signed int: 7, default: 23); /* { dg-error "two
compatible types" } */
+
+ /* No matching association. */
+ _Generic (n, void *: 5); /* { dg-error "not compatible with any
association" } */
+}
--
1.7.7.6