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  <tro...@redhat.com>

        * c-common.h (enum rid) <RID_GENERIC>: New constant.
        * c-common.c (c_common_reswords): Add _Generic.

2012-07-27  Tom Tromey  <tro...@redhat.com>

        * 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  <tro...@redhat.com>

        * 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

Reply via email to