diff mbox

C/C++ PATCH to add __typeof_noqual (PR c/65455, c/39985)

Message ID 20170626150918.GG4341@redhat.com
State New
Headers show

Commit Message

Marek Polacek June 26, 2017, 3:09 p.m. UTC
On Fri, Jun 23, 2017 at 04:27:47PM +0000, Joseph Myers wrote:
> On Fri, 23 Jun 2017, Marek Polacek wrote:
> 
> > You'll also see that I dropped all qualifiers for __auto_type.  But I actually
> > couldn't trigger the
> > init_type = c_build_qualified_type (init_type, TYPE_UNQUALIFIED);
> > line in c_parser_declaration_or_fndef (even when running the whole testsuite)
> > so I'm not convinced it makes any difference.
> 
> It looks like it would only make a difference, in the present code, for 
> the case of an atomic register variable, or bit-field in an atomic 
> structure, as the initializer.  Those are the cases where 

Ah, right.  But since __auto_type doesn't work with bit-fields, I only
tested the register variant.

> convert_lvalue_to_rvalue would not return a non-atomic result, given an 
> atomic argument.  With the proposed change, it should apply to any 
> qualified lvalue used as the initializer.
 
Right.

> > @@ -506,6 +508,7 @@ const struct c_common_resword c_common_reswords[] =
> >    { "typename",		RID_TYPENAME,	D_CXXONLY | D_CXXWARN },
> >    { "typeid",		RID_TYPEID,	D_CXXONLY | D_CXXWARN },
> >    { "typeof",		RID_TYPEOF,	D_ASM | D_EXT },
> > +  { "typeof_noqual",	RID_TYPEOF_NOQUAL, D_ASM | D_EXT },
> >    { "union",		RID_UNION,	0 },
> >    { "unsigned",		RID_UNSIGNED,	0 },
> >    { "using",		RID_USING,	D_CXXONLY | D_CXXWARN },
> 
> I don't think we should have this keyword variant.

Ok, dropped.

> I think there should be tests of the change to __auto_type.

I've added one.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2017-06-26  Marek Polacek  <polacek@redhat.com>
	    Richard Henderson  <rth@redhat.com>

	PR c/65455
	PR c/39985
	* c-common.c (c_common_reswords): Add __typeof_noqual and
	__typeof_noqual__.
	(keyword_begins_type_specifier): Handle RID_TYPEOF_NOQUAL.
	* c-common.h (enum rid): Add RID_TYPEOF_NOQUAL.

	* c-parser.c (c_keyword_starts_typename): Handle RID_TYPEOF_NOQUAL.
	(c_token_starts_declspecs): Likewise.
	(c_parser_declaration_or_fndef): Always strip all qualifiers for
	__auto_type.
	(c_parser_declspecs): Handle RID_TYPEOF_NOQUAL.
	(c_parser_typeof_specifier): Handle RID_TYPEOF_NOQUAL by dropping
	all the qualifiers.
	(c_parser_objc_selector): Handle RID_TYPEOF_NOQUAL.

	* parser.c (cp_keyword_starts_decl_specifier_p): Handle RID_TYPEOF_NOQUAL.
	(cp_parser_simple_type_specifier): Handle RID_TYPEOF_NOQUAL by dropping
	all the qualifiers.

	* doc/extend.texi: Document __typeof_noqual.

	* c-c++-common/typeof-noqual-1.c: New test.
	* c-c++-common/typeof-noqual-2.c: New test.
	* gcc.dg/typeof-noqual-1.c: New test.
	* gcc.dg/auto-type-3.c: New test.


	Marek
diff mbox

Patch

diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index f6a9d05..7993de2 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -433,6 +433,8 @@  const struct c_common_resword c_common_reswords[] =
   { "__transaction_cancel", RID_TRANSACTION_CANCEL, 0 },
   { "__typeof",		RID_TYPEOF,	0 },
   { "__typeof__",	RID_TYPEOF,	0 },
+  { "__typeof_noqual",	RID_TYPEOF_NOQUAL, 0 },
+  { "__typeof_noqual__", RID_TYPEOF_NOQUAL, 0 },
   { "__underlying_type", RID_UNDERLYING_TYPE, D_CXXONLY },
   { "__volatile",	RID_VOLATILE,	0 },
   { "__volatile__",	RID_VOLATILE,	0 },
@@ -7511,6 +7513,7 @@  keyword_begins_type_specifier (enum rid keyword)
     case RID_SAT:
     case RID_COMPLEX:
     case RID_TYPEOF:
+    case RID_TYPEOF_NOQUAL:
     case RID_STRUCT:
     case RID_CLASS:
     case RID_UNION:
diff --git gcc/c-family/c-common.h gcc/c-family/c-common.h
index f3d051a..ad00ae8 100644
--- gcc/c-family/c-common.h
+++ gcc/c-family/c-common.h
@@ -100,8 +100,9 @@  enum rid
   /* C extensions */
   RID_ASM,       RID_TYPEOF,   RID_ALIGNOF,  RID_ATTRIBUTE,  RID_VA_ARG,
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
-  RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	     RID_BUILTIN_SHUFFLE,
-  RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
+  RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,
+  RID_BUILTIN_SHUFFLE, RID_DFLOAT32, RID_DFLOAT64,  RID_DFLOAT128,
+  RID_TYPEOF_NOQUAL,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
   RID_FLOAT16,
diff --git gcc/c/c-parser.c gcc/c/c-parser.c
index f8fbc92..eb6cfad 100644
--- gcc/c/c-parser.c
+++ gcc/c/c-parser.c
@@ -495,6 +495,7 @@  c_keyword_starts_typename (enum rid keyword)
     case RID_STRUCT:
     case RID_UNION:
     case RID_TYPEOF:
+    case RID_TYPEOF_NOQUAL:
     case RID_CONST:
     case RID_ATOMIC:
     case RID_VOLATILE:
@@ -671,6 +672,7 @@  c_token_starts_declspecs (c_token *token)
 	case RID_STRUCT:
 	case RID_UNION:
 	case RID_TYPEOF:
+	case RID_TYPEOF_NOQUAL:
 	case RID_CONST:
 	case RID_VOLATILE:
 	case RID_RESTRICT:
@@ -1875,8 +1877,8 @@  c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 			      " initializer");
 		  init = convert_lvalue_to_rvalue (init_loc, init, true, true);
 		  tree init_type = TREE_TYPE (init.value);
-		  /* As with typeof, remove all qualifiers from atomic types.  */
-		  if (init_type != error_mark_node && TYPE_ATOMIC (init_type))
+		  /* As with __typeof_noqual, remove all qualifiers.  */
+		  if (init_type != error_mark_node)
 		    init_type
 		      = c_build_qualified_type (init_type, TYPE_UNQUALIFIED);
 		  bool vm_type = variably_modified_type_p (init_type,
@@ -2557,6 +2559,7 @@  c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 	  declspecs_add_type (loc, specs, t);
 	  break;
 	case RID_TYPEOF:
+	case RID_TYPEOF_NOQUAL:
 	  /* ??? The old parser rejected typeof after other type
 	     specifiers, but is a syntax error the best way of
 	     handling this?  */
@@ -3220,7 +3223,10 @@  c_parser_typeof_specifier (c_parser *parser)
   ret.spec = error_mark_node;
   ret.expr = NULL_TREE;
   ret.expr_const_operands = true;
-  gcc_assert (c_parser_next_token_is_keyword (parser, RID_TYPEOF));
+
+  enum rid keyword = c_parser_peek_token (parser)->keyword;
+  gcc_assert (keyword == RID_TYPEOF || keyword == RID_TYPEOF_NOQUAL);
+
   c_parser_consume_token (parser);
   c_inhibit_evaluation_warnings++;
   in_typeof++;
@@ -3262,7 +3268,9 @@  c_parser_typeof_specifier (c_parser *parser)
       /* For use in macros such as those in <stdatomic.h>, remove all
 	 qualifiers from atomic types.  (const can be an issue for more macros
 	 using typeof than just the <stdatomic.h> ones.)  */
-      if (ret.spec != error_mark_node && TYPE_ATOMIC (ret.spec))
+      if (ret.spec != error_mark_node
+	  /* __typeof_noqual also drops the qualifiers.  */
+	  && (TYPE_ATOMIC (ret.spec) || keyword == RID_TYPEOF_NOQUAL))
 	ret.spec = c_build_qualified_type (ret.spec, TYPE_UNQUALIFIED);
     }
   c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
@@ -9746,6 +9754,7 @@  c_parser_objc_selector (c_parser *parser)
     case RID_ASM:
     case RID_SIZEOF:
     case RID_TYPEOF:
+    case RID_TYPEOF_NOQUAL:
     case RID_ALIGNOF:
     case RID_UNSIGNED:
     case RID_LONG:
diff --git gcc/cp/parser.c gcc/cp/parser.c
index c405fe5..79aae2a 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -976,6 +976,7 @@  cp_keyword_starts_decl_specifier_p (enum rid keyword)
       /* GNU extensions.  */ 
     case RID_ATTRIBUTE:
     case RID_TYPEOF:
+    case RID_TYPEOF_NOQUAL:
       /* C++0x extensions.  */
     case RID_DECLTYPE:
     case RID_UNDERLYING_TYPE:
@@ -16867,6 +16868,7 @@  cp_parser_simple_type_specifier (cp_parser* parser,
       break;
 
     case RID_TYPEOF:
+    case RID_TYPEOF_NOQUAL:
       /* Consume the `typeof' token.  */
       cp_lexer_consume_token (parser->lexer);
       /* Parse the operand to `typeof'.  */
@@ -16874,6 +16876,9 @@  cp_parser_simple_type_specifier (cp_parser* parser,
       /* If it is not already a TYPE, take its type.  */
       if (!TYPE_P (type))
 	type = finish_typeof (type);
+      /* If requested, make the type unqualified.  */
+      if (token->keyword == RID_TYPEOF_NOQUAL && type != error_mark_node)
+	type = cp_build_qualified_type (type, TYPE_UNQUALIFIED);
 
       if (decl_specs)
 	cp_parser_set_decl_spec_type (decl_specs, type,
diff --git gcc/doc/extend.texi gcc/doc/extend.texi
index 43f9ecf..acfd7e8 100644
--- gcc/doc/extend.texi
+++ gcc/doc/extend.texi
@@ -155,7 +155,8 @@  the value of an enumeration constant, the width of a bit-field, or
 the initial value of a static variable.
 
 If you don't know the type of the operand, you can still do this, but you
-must use @code{typeof} or @code{__auto_type} (@pxref{Typeof}).
+must use @code{typeof}, @code{__typeof_noqual}, or @code{__auto_type}
+(@pxref{Typeof}).
 
 In G++, the result value of a statement expression undergoes array and
 function pointer decay, and is returned by value to the enclosing
@@ -642,6 +643,7 @@  myopen (const char *path, int oflag, ...)
 @node Typeof
 @section Referring to a Type with @code{typeof}
 @findex typeof
+@findex __typeof_noqual
 @findex sizeof
 @cindex macros, types of arguments
 
@@ -694,6 +696,22 @@  arithmetic type and evaluates each of its arguments exactly once:
     _a > _b ? _a : _b; @})
 @end smallexample
 
+@code{__typeof_noqual} behaves the same except that it strips type qualifiers
+such as @code{const} and @code{volatile}, if given an expression.  This can
+be useful for certain macros when passed const arguments:
+
+@smallexample
+#define MAX(__x, __y)			\
+  (@{					\
+  __typeof_noqual(__x) __ret = __x;	\
+  if (__y > __ret) __ret = __y;		\
+    __ret;				\
+  @})
+
+const int ci = 5;
+MAX (ci, 12);
+@end smallexample
+
 @cindex underscores in variables in macros
 @cindex @samp{_} in variables in macros
 @cindex local variables in macros
diff --git gcc/testsuite/c-c++-common/typeof-noqual-1.c gcc/testsuite/c-c++-common/typeof-noqual-1.c
index e69de29..455e51f 100644
--- gcc/testsuite/c-c++-common/typeof-noqual-1.c
+++ gcc/testsuite/c-c++-common/typeof-noqual-1.c
@@ -0,0 +1,42 @@ 
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "-pedantic-errors" } */
+
+void
+foo (void)
+{
+  int i = 0;
+  const int ci = 0;
+  volatile int vi = 0;
+
+  __typeof(i) *ip = 0;
+  __typeof(ci) *cip = 0;
+  __typeof(vi) *vip = 0;
+
+  __typeof_noqual(i) *nip = 0;
+  __typeof_noqual(ci) *ncip = 0;
+  __typeof_noqual(vi) *nvip = 0;
+
+  __typeof_noqual__(i) *nip2 = 0;
+  __typeof_noqual__(ci) *ncip2 = 0;
+  __typeof_noqual__(vi) *nvip2 = 0;
+
+  ip = cip;		/* { dg-error "assignment discards|invalid conversion" } */
+  ip = vip;		/* { dg-error "assignment discards|invalid conversion" } */
+
+  ip = nip;
+  ip = ncip;
+  ip = nvip;
+
+  ip = nip2;
+  ip = ncip2;
+  ip = nvip2;
+
+  ncip = cip;		/* { dg-error "assignment discards|invalid conversion" } */
+  nvip = vip;		/* { dg-error "assignment discards|invalid conversion" } */
+  ncip2 = cip;		/* { dg-error "assignment discards|invalid conversion" } */
+  nvip2 = vip;		/* { dg-error "assignment discards|invalid conversion" } */
+
+  nip = ip;
+  nip2 = ip;
+}
diff --git gcc/testsuite/c-c++-common/typeof-noqual-2.c gcc/testsuite/c-c++-common/typeof-noqual-2.c
index e69de29..1a27e44 100644
--- gcc/testsuite/c-c++-common/typeof-noqual-2.c
+++ gcc/testsuite/c-c++-common/typeof-noqual-2.c
@@ -0,0 +1,32 @@ 
+/* PR c/65455 */
+/* { dg-do compile } */
+
+const int g(void);
+
+#define MAX(__x, __y)			\
+  ({					\
+  __typeof_noqual(__x) __ret = __x;	\
+  if (__y > __ret) __ret = __y;		\
+    __ret;				\
+  })
+
+void
+fn (void)
+{
+  const int ci = 5;
+  __typeof_noqual (({ ci; })) n1;
+  __typeof_noqual (ci) n2;
+  __typeof (g ()) n4;
+  __typeof_noqual (g ()) n3;
+
+  typedef __typeof_noqual(ci) T;
+  T n5;
+
+  n1 = 5;
+  n2 = 5;
+  n3 = 5;
+  n4 = 5;
+  n5 = 5;
+
+  MAX (ci, 12);
+}
diff --git gcc/testsuite/gcc.dg/auto-type-3.c gcc/testsuite/gcc.dg/auto-type-3.c
index e69de29..79479c8 100644
--- gcc/testsuite/gcc.dg/auto-type-3.c
+++ gcc/testsuite/gcc.dg/auto-type-3.c
@@ -0,0 +1,25 @@ 
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+struct S
+{
+  int k;
+};
+
+void
+foo (void)
+{
+  _Atomic register const int a = 3;
+  const int b = 16;
+  const struct S s;
+  int *const c = 0;
+
+  __auto_type i = a;
+  i++;
+  __auto_type j = b;
+  j++;
+  __auto_type k = s.k;
+  k++;
+  __auto_type l = c;
+  l++;
+}
diff --git gcc/testsuite/gcc.dg/typeof-noqual-1.c gcc/testsuite/gcc.dg/typeof-noqual-1.c
index e69de29..ec72506 100644
--- gcc/testsuite/gcc.dg/typeof-noqual-1.c
+++ gcc/testsuite/gcc.dg/typeof-noqual-1.c
@@ -0,0 +1,23 @@ 
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "-Wrestrict" } */
+
+int *restrict t;
+
+void
+bar (__typeof_noqual (t) p, __typeof_noqual (t) q)
+{
+}
+
+void
+baz (__typeof (t) p, __typeof (t) q)
+{
+}
+
+void
+foo (void)
+{
+  int i = 42;
+  bar (&i, &i);
+  baz (&i, &i); /* { dg-warning "passing argument 1 to restrict-qualified parameter aliases" } */
+}