This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: RFA: implement C11 _Generic


I have now revised this patch from a year ago in line with my
understanding of how _Generic ought to handle the various special
cases (selector undergoes lvalue-to-rvalue conversion, and decay of
functions and arrays to pointers, because nothing says it doesn't -
"The controlling expression of a generic selection was very carefully
not added to the list of contexts in which lvalue conversion is not
done and type qualification is discarded", the minutes say - and no
rvalues can have qualified type), which seems to accord with the
committee discussion in the Delft minutes, added corresponding
testcases, and committed this patch.  Bootstrapped with no regressions
on x86_64-unknown-linux-gnu.

The committee discussion includes a further point to ensure rvalues
can have qualified type: treating qualified function return types the
same as unqualified, like the way qualified function parameter types
are treated.  If that gets adopted, some further changes will be
needed elsewhere (but all those will do is cause some code to be
accepted that's currently rejected).

c-family:
2013-07-23  Tom Tromey  <tromey@redhat.com>

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

c:
2013-07-23  Tom Tromey  <tromey@redhat.com>
	    Joseph Myers  <joseph@codesourcery.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.

testsuite:
2013-07-23  Tom Tromey  <tromey@redhat.com>
	    Joseph Myers  <joseph@codesourcery.com>

	* gcc.dg/c11-generic-1.c: New file.
	* gcc.dg/c11-generic-2.c: New file.

Index: c/c-parser.c
===================================================================
--- c/c-parser.c	(revision 201131)
+++ c/c-parser.c	(working copy)
@@ -6232,7 +6232,226 @@ c_parser_get_builtin_args (c_parser *parser, const
   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;
+};
+
+/* 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> associations = vNULL;
+  struct c_expr selector, error_expr;
+  tree selector_type;
+  struct c_generic_association matched_assoc;
+  bool match_found = false;
+  location_t generic_loc, selector_loc;
+
+  error_expr.original_code = ERROR_MARK;
+  error_expr.original_type = NULL;
+  error_expr.value = error_mark_node;
+  matched_assoc.type_location = UNKNOWN_LOCATION;
+  matched_assoc.type = NULL_TREE;
+  matched_assoc.expression = error_expr;
+
+  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);
+  selector = default_function_array_conversion (selector_loc, selector);
+  c_inhibit_evaluation_warnings--;
+
+  if (selector.value == error_mark_node)
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return selector;
+    }
+  selector_type = TREE_TYPE (selector.value);
+  /* In ISO C terms, rvalues (including the controlling expression of
+     _Generic) do not have qualified types.  */
+  if (TREE_CODE (selector_type) != ARRAY_TYPE)
+    selector_type = TYPE_MAIN_VARIANT (selector_type);
+  /* In ISO C terms, _Noreturn is not part of the type of expressions
+     such as &abort, but in GCC it is represented internally as a type
+     qualifier.  */
+  if (FUNCTION_POINTER_TYPE_P (selector_type)
+      && TYPE_QUALS (TREE_TYPE (selector_type)) != TYPE_UNQUALIFIED)
+    selector_type
+      = build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE (selector_type)));
+
+  if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return error_expr;
+    }
+
+  while (1)
+    {
+      struct c_generic_association assoc, *iter;
+      unsigned 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)
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      goto error_exit;
+	    }
+	  assoc.type = groktypename (type_name, NULL, NULL);
+	  if (assoc.type == error_mark_node)
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      goto error_exit;
+	    }
+
+	  if (TREE_CODE (assoc.type) == FUNCTION_TYPE)
+	    error_at (assoc.type_location,
+		      "%<_Generic%> association has function type");
+	  else 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 %<:%>"))
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  goto error_exit;
+	}
+
+      assoc.expression = c_parser_expr_no_commas (parser, NULL);
+      if (assoc.expression.value == error_mark_node)
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  goto error_exit;
+	}
+
+      for (ix = 0; associations.iterate (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 = true;
+	    }
+	}
+      else if (comptypes (assoc.type, selector_type))
+	{
+	  if (!match_found || matched_assoc.type == NULL_TREE)
+	    {
+	      matched_assoc = assoc;
+	      match_found = true;
+	    }
+	  else
+	    {
+	      error_at (assoc.type_location,
+			"%<_Generic> selector matches multiple associations");
+	      inform (matched_assoc.type_location,
+		      "other match is here");
+	    }
+	}
+
+      associations.safe_push (assoc);
+
+      if (c_parser_peek_token (parser)->type != CPP_COMMA)
+	break;
+      c_parser_consume_token (parser);
+    }
+
+  associations.release ();
+
+  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      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;
+    }
+
+  return matched_assoc.expression;
+
+ error_exit:
+  associations.release ();
+  return error_expr;
+}
+
 /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2).
 
    postfix-expression:
@@ -6255,6 +6474,7 @@ c_parser_get_builtin_args (c_parser *parser, const
      constant
      string-literal
      ( expression )
+     generic-selection
 
    GNU extensions:
 
@@ -6823,6 +7043,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;
Index: c-family/c-common.c
===================================================================
--- c-family/c-common.c	(revision 201131)
+++ c-family/c-common.c	(working copy)
@@ -412,6 +412,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 },
Index: c-family/c-common.h
===================================================================
--- c-family/c-common.h	(revision 201131)
+++ c-family/c-common.h	(working copy)
@@ -105,7 +105,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.  */
Index: testsuite/gcc.dg/c11-generic-1.c
===================================================================
--- testsuite/gcc.dg/c11-generic-1.c	(revision 0)
+++ testsuite/gcc.dg/c11-generic-1.c	(revision 0)
@@ -0,0 +1,57 @@
+/* Test C11 _Generic.  Valid uses.  */
+/* { dg-do run } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+_Noreturn extern void exit (int);
+_Noreturn 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);
+
+  /* Qualifiers are removed for the purpose of type matching.  */
+  const int cn = 0;
+  check (_Generic (cn, int: 0, default: n++));
+  check (n);
+  check (_Generic ((const int) n, int: 0, default: n++));
+  check (n);
+
+  /* Arrays decay to pointers.  */
+  int a[1];
+  const int ca[1];
+  check (_Generic (a, int *: 0, const int *: n++));
+  check (n);
+  check (_Generic (ca, const int *: 0, int *: n++));
+  check (n);
+
+  /* Functions decay to pointers.  */
+  extern void f (void);
+  check (_Generic (f, void (*) (void): 0, default: n++));
+  check (n);
+
+  /* _Noreturn is not part of the function type.  */
+  check (_Generic (&abort, void (*) (void): 0, default: n++));
+  check (n);
+
+  /* Integer promotions do not occur.  */
+  short s;
+  check (_Generic (s, short: 0, int: n++));
+  check (n);
+
+  exit (0);
+}
Index: testsuite/gcc.dg/c11-generic-2.c
===================================================================
--- testsuite/gcc.dg/c11-generic-2.c	(revision 0)
+++ testsuite/gcc.dg/c11-generic-2.c	(revision 0)
@@ -0,0 +1,27 @@
+/* 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" } */
+
+  /* Type must be object type.  */
+  _Generic (n, void (void): 0, default: 1); /* { dg-error "function 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" } */
+}

-- 
Joseph S. Myers
joseph@codesourcery.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]