diff mbox series

[C] Support typename as selector in _Generic

Message ID ffd3c7b1b0c71e13f40471aeef643b9c9e3c0353.camel@tugraz.at
State New
Headers show
Series [C] Support typename as selector in _Generic | expand

Commit Message

Martin Uecker Aug. 5, 2023, 4:33 p.m. UTC
Clang now has an extension which accepts a typename for
_Generic.  This is simple to implement and is useful.

Do we want this?

Clang calls it a "Clang extension" in the pedantic
warning.  I changed it to "an extension"  I am not
sure what the policy is.

Do we need an extra warning option? Clang has one.

No documentation so far.

Bootstrapped and regression tested on x86_64-pc-linux-gnu.

Martin


    c: Support typename as selector in _Generic
    
    Support typenames as first argument to _Generic which is an
    extension supported by Clang. It makes it easier to test
    for types with qualifiers in combination with typeof.
    
    gcc/c/:
            * c-parser.cc (c_parser_generic_selection): Support typename
            in _Generic selector.
    
    gcc/testsuite/:
            * gnu2x-generic.c: New test.
diff mbox series

Patch

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 57a01dc2fa3..9aea2425294 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -9312,30 +9312,51 @@  c_parser_generic_selection (c_parser *parser)
   if (!parens.require_open (parser))
     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)
+  if (c_token_starts_typename (c_parser_peek_token (parser)))
     {
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-      return selector;
-    }
-  mark_exp_read (selector.value);
-  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)));
+      /* Language extension introduced by Clang.  */
+      pedwarn (selector_loc, OPT_Wpedantic, "passing a type argument as "
+	       "first argument to %<_Generic%> is an extension");
+      struct c_type_name *type_name;
+      c_inhibit_evaluation_warnings++;
+      type_name = c_parser_type_name (parser);
+      c_inhibit_evaluation_warnings--;
+      if (NULL == type_name)
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return error_expr;
+	}
+      /* Qualifiers are preserved.  */
+      selector_type = groktypename (type_name, NULL, NULL);
+    }
+  else
+    {
+      c_inhibit_evaluation_warnings++;
+      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;
+	}
+      mark_exp_read (selector.value);
+      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 %<,%>"))
     {
@@ -9401,7 +9422,7 @@  c_parser_generic_selection (c_parser *parser)
       assoc.expression = c_parser_expr_no_commas (parser, NULL);
 
       if (!match)
-	  c_inhibit_evaluation_warnings--;
+	c_inhibit_evaluation_warnings--;
 
       if (assoc.expression.value == error_mark_node)
 	{
diff --git a/gcc/testsuite/gcc.dg/gnu2x-generic.c b/gcc/testsuite/gcc.dg/gnu2x-generic.c
new file mode 100644
index 00000000000..82b09578072
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu2x-generic.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+
+_Static_assert(_Generic(const int, const int: 1, int: 0), "");
+_Static_assert(_Generic(      int, const int: 0, int: 1), "");
+_Static_assert(_Generic(int[4], int[4]: 1), "");
+_Static_assert(_Generic(typeof(int[4]), int[4]: 1), "");
+
+void foo(int n)
+{
+	_Static_assert(_Generic(int[n++], int[4]: 1), "");
+}
+
+#pragma GCC diagnostic warning "-Wpedantic"
+_Static_assert(_Generic(int[4], int[4]: 1), "");	/* { dg-warning "extension" } */
+
+