diff mbox series

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

Message ID 20170831152515.GR17069@redhat.com
State New
Headers show
Series C/C++ PATCH to add __remove_qualifiers (PR c/65455, c/39985) | expand

Commit Message

Marek Polacek Aug. 31, 2017, 3:25 p.m. UTC
After a long time, I'm finally sending the revisited patch dealing with these
two PRs.  To quickly recap, users were looking for a typeof variant that
strips type qualifiers.  I sent a path adding __typeof_noqual, but a discussion
ensued and it's been concluded that we'd rather go a different way, i.e. add
__remove_qualifiers, which takes a typename, and strips type qualifiers.  That
is my understanding anyway.  Here's a patch implementing just that, for both
C and C++ FEs.

Here's a link to the previous discussion:
https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01146.html

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

2017-08-31  Marek Polacek  <polacek@redhat.com>

	PR c/39985
	PR c/65455
	* c-common.c (c_common_reswords): Add __remove_qualifiers and
	__remove_qualifiers__.
	(keyword_begins_type_specifier): Handle RID_REMOVE_QUALS.
	* c-common.h (enum rid): Add RID_REMOVE_QUALS.

	* c-decl.c (start_struct): Also check in_remove_qualifiers.
	(finish_struct): Likewise.
	(start_enum): Likewise.
	(finish_enum): Likewise.
	* c-parser.c (c_keyword_starts_typename): Handle RID_REMOVE_QUALS.
	(c_token_starts_declspecs): Likewise.
	(c_parser_declaration_or_fndef): For __auto_type, remove all type
	qualifiers.
	(c_parser_declspecs): Handle RID_REMOVE_QUALS.
	(c_parser_remove_qualifiers_specifier): New function.
	(c_parser_objc_selector): Handle RID_REMOVE_QUALS.
	* c-tree.h (enum c_typespec_kind): Update a comment.
	Declare in_remove_qualifiers.
	* c-typeck.c (in_remove_qualifiers): New global variable.
	(build_external_ref): Also check in_remove_qualifiers.
	(struct maybe_used_decl): Likewise.
	(record_maybe_used_decl): Likewise.
	(pop_maybe_used): Likewise.

	* parser.c (cp_keyword_starts_decl_specifier_p): Handle
	RID_REMOVE_QUALS.
	(cp_parser_simple_type_specifier): Likewise.
	(cp_parser_sizeof_operand): For __remove_qualifiers, remove all type
	qualifiers.

	* doc/extend.texi: Document __remove_qualifiers.

	* c-c++-common/remove-quals-1.c: New test.
	* c-c++-common/remove-quals-2.c: New test.
	* c-c++-common/remove-quals-3.c: New test.
	* c-c++-common/remove-quals-4.c: New test.
	* g++.dg/ext/remove-quals-1.C: New test.
	* g++.dg/ext/remove-quals-2.C: New test.
	* gcc.dg/auto-type-3.c: New test.
	* gcc.dg/remove-quals-1.c: New test.
	* gcc.dg/remove-quals-2.c: New test.


	Marek

Comments

Joseph Myers Aug. 31, 2017, 4:38 p.m. UTC | #1
I think the documentation needs to say (and the tests need to test) that 
this produces a non-atomic type (like lvalue-to-rvalue conversion), if 
that's the intent for how it handles atomic types, since _Atomic is 
syntactically a qualifier but largely not treated like one in the 
standard.
Marek Polacek Sept. 1, 2017, 12:46 p.m. UTC | #2
On Thu, Aug 31, 2017 at 04:38:27PM +0000, Joseph Myers wrote:
> I think the documentation needs to say (and the tests need to test) that 
> this produces a non-atomic type (like lvalue-to-rvalue conversion), if 
> that's the intent for how it handles atomic types, since _Atomic is 
> syntactically a qualifier but largely not treated like one in the 
> standard.

True, updated patch here:

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

2017-09-01  Marek Polacek  <polacek@redhat.com>

	PR c/39985
	PR c/65455
	* c-common.c (c_common_reswords): Add __remove_qualifiers and
	__remove_qualifiers__.
	(keyword_begins_type_specifier): Handle RID_REMOVE_QUALS.
	* c-common.h (enum rid): Add RID_REMOVE_QUALS.

	* c-decl.c (start_struct): Also check in_remove_qualifiers.
	(finish_struct): Likewise.
	(start_enum): Likewise.
	(finish_enum): Likewise.
	* c-parser.c (c_keyword_starts_typename): Handle RID_REMOVE_QUALS.
	(c_token_starts_declspecs): Likewise.
	(c_parser_declaration_or_fndef): For __auto_type, remove all type
	qualifiers.
	(c_parser_declspecs): Handle RID_REMOVE_QUALS.
	(c_parser_remove_qualifiers_specifier): New function.
	(c_parser_objc_selector): Handle RID_REMOVE_QUALS.
	* c-tree.h (enum c_typespec_kind): Update a comment.
	Declare in_remove_qualifiers.
	* c-typeck.c (in_remove_qualifiers): New global variable.
	(build_external_ref): Also check in_remove_qualifiers.
	(struct maybe_used_decl): Likewise.
	(record_maybe_used_decl): Likewise.
	(pop_maybe_used): Likewise.

	* parser.c (cp_keyword_starts_decl_specifier_p): Handle
	RID_REMOVE_QUALS.
	(cp_parser_simple_type_specifier): Likewise.
	(cp_parser_sizeof_operand): For __remove_qualifiers, remove all type
	qualifiers.

	* doc/extend.texi: Document __remove_qualifiers.

	* c-c++-common/remove-quals-1.c: New test.
	* c-c++-common/remove-quals-2.c: New test.
	* c-c++-common/remove-quals-3.c: New test.
	* c-c++-common/remove-quals-4.c: New test.
	* g++.dg/ext/remove-quals-1.C: New test.
	* g++.dg/ext/remove-quals-2.C: New test.
	* gcc.dg/auto-type-3.c: New test.
	* gcc.dg/remove-quals-1.c: New test.
	* gcc.dg/remove-quals-2.c: New test.
	* gcc.dg/remove-quals-3.c: New test.

diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index d959dbc25bb..ae92ff440f6 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -423,6 +423,8 @@ const struct c_common_resword c_common_reswords[] =
   { "__null",		RID_NULL,	0 },
   { "__real",		RID_REALPART,	0 },
   { "__real__",		RID_REALPART,	0 },
+  { "__remove_qualifiers", RID_REMOVE_QUALS, 0 },
+  { "__remove_qualifiers__", RID_REMOVE_QUALS, 0 },
   { "__restrict",	RID_RESTRICT,	0 },
   { "__restrict__",	RID_RESTRICT,	0 },
   { "__signed",		RID_SIGNED,	0 },
@@ -7525,6 +7527,7 @@ keyword_begins_type_specifier (enum rid keyword)
     case RID_CLASS:
     case RID_UNION:
     case RID_ENUM:
+    case RID_REMOVE_QUALS:
       return true;
     default:
       if (keyword >= RID_FIRST_INT_N
diff --git gcc/c-family/c-common.h gcc/c-family/c-common.h
index 8e367680600..e726aa8844b 100644
--- gcc/c-family/c-common.h
+++ gcc/c-family/c-common.h
@@ -101,7 +101,7 @@ enum rid
   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_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,  RID_REMOVE_QUALS,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
   RID_FLOAT16,
diff --git gcc/c/c-decl.c gcc/c/c-decl.c
index d526f0e88e4..b9cd5f8cf56 100644
--- gcc/c/c-decl.c
+++ gcc/c/c-decl.c
@@ -7516,12 +7516,14 @@ start_struct (location_t loc, enum tree_code code, tree name,
      within a statement expr used within sizeof, et. al.  This is not
      terribly serious as C++ doesn't permit statement exprs within
      sizeof anyhow.  */
-  if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof))
+  if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof
+			  || in_remove_qualifiers))
     warning_at (loc, OPT_Wc___compat,
 		"defining type in %qs expression is invalid in C++",
-		(in_sizeof
-		 ? "sizeof"
-		 : (in_typeof ? "typeof" : "alignof")));
+		(in_sizeof ? "sizeof"
+		 : (in_typeof ? "typeof"
+			      : (in_alignof ? "alignof"
+					    : "__remove_qualifiers"))));
 
   return ref;
 }
@@ -8159,7 +8161,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
      struct_types.  */
   if (warn_cxx_compat
       && struct_parse_info != NULL
-      && !in_sizeof && !in_typeof && !in_alignof)
+      && !in_sizeof && !in_typeof && !in_alignof && !in_remove_qualifiers)
     struct_parse_info->struct_types.safe_push (t);
 
   return t;
@@ -8235,12 +8237,14 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name)
   /* FIXME: This will issue a warning for a use of a type defined
      within sizeof in a statement expr.  This is not terribly serious
      as C++ doesn't permit statement exprs within sizeof anyhow.  */
-  if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof))
+  if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof
+			  || in_remove_qualifiers))
     warning_at (loc, OPT_Wc___compat,
 		"defining type in %qs expression is invalid in C++",
-		(in_sizeof
-		 ? "sizeof"
-		 : (in_typeof ? "typeof" : "alignof")));
+		(in_sizeof ? "sizeof"
+		 : (in_typeof ? "typeof"
+			      : (in_alignof ? "alignof"
+					    : "__remove_qualifiers"))));
 
   return enumtype;
 }
@@ -8395,7 +8399,7 @@ finish_enum (tree enumtype, tree values, tree attributes)
      struct_types.  */
   if (warn_cxx_compat
       && struct_parse_info != NULL
-      && !in_sizeof && !in_typeof && !in_alignof)
+      && !in_sizeof && !in_typeof && !in_alignof && !in_remove_qualifiers)
     struct_parse_info->struct_types.safe_push (enumtype);
 
   return enumtype;
diff --git gcc/c/c-parser.c gcc/c/c-parser.c
index f4e1cf6aa0c..f65d5f17ebb 100644
--- gcc/c/c-parser.c
+++ gcc/c/c-parser.c
@@ -504,6 +504,7 @@ c_keyword_starts_typename (enum rid keyword)
     case RID_ACCUM:
     case RID_SAT:
     case RID_AUTO_TYPE:
+    case RID_REMOVE_QUALS:
       return true;
     default:
       if (keyword >= RID_FIRST_INT_N
@@ -681,6 +682,7 @@ c_token_starts_declspecs (c_token *token)
 	case RID_ALIGNAS:
 	case RID_ATOMIC:
 	case RID_AUTO_TYPE:
+	case RID_REMOVE_QUALS:
 	  return true;
 	default:
 	  if (token->keyword >= RID_FIRST_INT_N
@@ -1361,6 +1363,7 @@ static struct c_typespec c_parser_enum_specifier (c_parser *);
 static struct c_typespec c_parser_struct_or_union_specifier (c_parser *);
 static tree c_parser_struct_declaration (c_parser *);
 static struct c_typespec c_parser_typeof_specifier (c_parser *);
+static struct c_typespec c_parser_remove_qualifiers_specifier (c_parser *);
 static tree c_parser_alignas_specifier (c_parser *);
 static struct c_declarator *c_parser_direct_declarator (c_parser *, bool,
 							c_dtr_syn, bool *);
@@ -2039,8 +2042,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))
+		  /* Remove all qualifiers from all types.  */
+		  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,
@@ -2732,6 +2735,14 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 	  t = c_parser_typeof_specifier (parser);
 	  declspecs_add_type (loc, specs, t);
 	  break;
+	case RID_REMOVE_QUALS:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_remove_qualifiers_specifier (parser);
+	  declspecs_add_type (loc, specs, t);
+	  break;
 	case RID_ATOMIC:
 	  /* C parser handling of Objective-C constructs needs
 	     checking for correct lvalue-to-rvalue conversions, and
@@ -3435,6 +3446,55 @@ c_parser_typeof_specifier (c_parser *parser)
   return ret;
 }
 
+/* Parse a __remove_qualifiers specifier (a GNU extension).
+
+   remove-qualifiers-specifier:
+     __remove_qualifiers ( type-name )
+*/
+
+static struct c_typespec
+c_parser_remove_qualifiers_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  ret.kind = ctsk_typeof;
+  ret.spec = error_mark_node;
+  ret.expr = NULL_TREE;
+  ret.expr_const_operands = true;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_REMOVE_QUALS));
+
+  /* Consume '__remove_qualifiers'.  */
+  c_parser_consume_token (parser);
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    return ret;
+
+  /* Only accept a type-name as an argument.  */
+  if (c_parser_next_tokens_start_typename (parser, cla_prefer_id))
+    {
+      /* Do not warn about problems with the expression.  */
+      c_inhibit_evaluation_warnings++;
+      in_remove_qualifiers++;
+      struct c_type_name *type = c_parser_type_name (parser);
+      /* Go back to evaluating expressions.  */
+      c_inhibit_evaluation_warnings--;
+      in_remove_qualifiers--;
+      if (type != NULL)
+	{
+	  ret.spec = groktypename (type, &ret.expr, &ret.expr_const_operands);
+	  pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE));
+	  /* Remove all type qualifiers.  */
+	  if (ret.spec != error_mark_node)
+	    ret.spec = c_build_qualified_type (ret.spec, TYPE_UNQUALIFIED);
+	}
+    }
+  else
+    error_at (c_parser_peek_token (parser)->location,
+	      "%<__remove_qualifiers%> can only be applied to a type");
+  parens.skip_until_found_close (parser);
+  return ret;
+}
+
 /* Parse an alignment-specifier.
 
    C11 6.7.5:
@@ -9879,7 +9939,7 @@ c_parser_objc_synchronized_statement (c_parser *parser)
        break continue return goto asm sizeof typeof __alignof
        unsigned long const short volatile signed restrict _Complex
        in out inout bycopy byref oneway int char float double void _Bool
-       _Atomic
+       _Atomic __remove_qualifiers
 
    ??? Why this selection of keywords but not, for example, storage
    class specifiers?  */
@@ -9944,6 +10004,7 @@ c_parser_objc_selector (c_parser *parser)
     case RID_INT_N_1:
     case RID_INT_N_2:
     case RID_INT_N_3:
+    case RID_REMOVE_QUALS:
       c_parser_consume_token (parser);
       return value;
     default:
diff --git gcc/c/c-tree.h gcc/c/c-tree.h
index 96c7ae7613f..e16c4185350 100644
--- gcc/c/c-tree.h
+++ gcc/c/c-tree.h
@@ -191,7 +191,7 @@ enum c_typespec_kind {
   ctsk_typedef,
   /* An ObjC-specific kind of type specifier.  */
   ctsk_objc,
-  /* A typeof specifier, or _Atomic ( type-name ).  */
+  /* A typeof specifier, _Atomic ( type-name ), or __remove_qualifiers.  */
   ctsk_typeof
 };
 
@@ -617,6 +617,7 @@ extern bool c_vla_unspec_p (tree x, tree fn);
 extern int in_alignof;
 extern int in_sizeof;
 extern int in_typeof;
+extern int in_remove_qualifiers;
 
 extern tree c_last_sizeof_arg;
 extern location_t c_last_sizeof_loc;
diff --git gcc/c/c-typeck.c gcc/c/c-typeck.c
index 135dd9d665c..ce720802e70 100644
--- gcc/c/c-typeck.c
+++ gcc/c/c-typeck.c
@@ -72,6 +72,9 @@ int in_sizeof;
 /* The level of nesting inside "typeof".  */
 int in_typeof;
 
+/* The level of nesting inside "__remove_qualifiers".  */
+int in_remove_qualifiers;
+
 /* The argument of last parsed sizeof expression, only to be tested
    if expr.original_code == SIZEOF_EXPR.  */
 tree c_last_sizeof_arg;
@@ -2782,7 +2785,7 @@ build_external_ref (location_t loc, tree id, bool fun, tree *type)
 
   if (TREE_CODE (ref) == FUNCTION_DECL && !in_alignof)
     {
-      if (!in_sizeof && !in_typeof)
+      if (!in_sizeof && !in_typeof && !in_remove_qualifiers)
 	C_DECL_USED (ref) = 1;
       else if (DECL_INITIAL (ref) == NULL_TREE
 	       && DECL_EXTERNAL (ref)
@@ -2838,7 +2841,7 @@ struct maybe_used_decl
 {
   /* The decl.  */
   tree decl;
-  /* The level seen at (in_sizeof + in_typeof).  */
+  /* The level seen at (in_sizeof + in_typeof + in_remove_qualifiers).  */
   int level;
   /* The next one at this level or above, or NULL.  */
   struct maybe_used_decl *next;
@@ -2856,7 +2859,7 @@ record_maybe_used_decl (tree decl)
 {
   struct maybe_used_decl *t = XOBNEW (&parser_obstack, struct maybe_used_decl);
   t->decl = decl;
-  t->level = in_sizeof + in_typeof;
+  t->level = in_sizeof + in_typeof + in_remove_qualifiers;
   t->next = maybe_used_decls;
   maybe_used_decls = t;
 }
@@ -2870,7 +2873,7 @@ void
 pop_maybe_used (bool used)
 {
   struct maybe_used_decl *p = maybe_used_decls;
-  int cur_level = in_sizeof + in_typeof;
+  int cur_level = in_sizeof + in_typeof + in_remove_qualifiers;
   while (p && p->level > cur_level)
     {
       if (used)
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 4bfae3655d5..2d4605d8726 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -973,9 +973,10 @@ cp_keyword_starts_decl_specifier_p (enum rid keyword)
     case RID_FLOAT:
     case RID_DOUBLE:
     case RID_VOID:
-      /* GNU extensions.  */ 
+      /* GNU extensions.  */
     case RID_ATTRIBUTE:
     case RID_TYPEOF:
+    case RID_REMOVE_QUALS:
       /* C++0x extensions.  */
     case RID_DECLTYPE:
     case RID_UNDERLYING_TYPE:
@@ -16960,6 +16961,24 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 
       return type;
 
+    case RID_REMOVE_QUALS:
+      /* Consume the `__remove_qualifiers' token.  */
+      cp_lexer_consume_token (parser->lexer);
+      /* Parse the operand to __remove_qualifiers`'.  */
+      type = cp_parser_sizeof_operand (parser, RID_REMOVE_QUALS);
+      if (!TYPE_P (type))
+	{
+	  error_at (token->location,
+		    "%<__remove_qualifiers%> can only be applied to a type");
+	  type = error_mark_node;
+	}
+      if (decl_specs)
+	cp_parser_set_decl_spec_type (decl_specs, type,
+				      token,
+				      /*type_definition_p=*/false);
+
+      return type;
+
     case RID_UNDERLYING_TYPE:
       type = cp_parser_trait_expr (parser, RID_UNDERLYING_TYPE);
       if (decl_specs)
@@ -27696,6 +27715,9 @@ cp_parser_sizeof_operand (cp_parser* parser, enum rid keyword)
 				 TYPENAME,
 				 /*initialized=*/0,
 				 /*attrlist=*/NULL);
+	  /* __remove_qualifiers removes all type qualifiers.  */
+	  if (keyword == RID_REMOVE_QUALS)
+	    expr = cp_build_qualified_type (expr, TYPE_UNQUALIFIED);
 	}
     }
 
diff --git gcc/doc/extend.texi gcc/doc/extend.texi
index 649be015dbb..8e6a7d8c395 100644
--- gcc/doc/extend.texi
+++ gcc/doc/extend.texi
@@ -29,6 +29,7 @@ extensions, accepted by GCC in C90 mode and in C++.
 * Nested Functions::    As in Algol and Pascal, lexical scoping of functions.
 * Constructing Calls::  Dispatching a call to another function.
 * Typeof::              @code{typeof}: referring to the type of an expression.
+* __remove_qualifiers::	Removing type qualifiers using @code{__remove_qualifiers}.
 * Conditionals::        Omitting the middle operand of a @samp{?:} expression.
 * __int128::		128-bit integers---@code{__int128}.
 * Long Long::           Double-word integers---@code{long long int}.
@@ -788,6 +789,38 @@ evaluated only once when using @code{__auto_type}, but twice if
 @code{typeof} is used.
 @end itemize
 
+@node __remove_qualifiers
+@section Removing type qualifiers using @code{__remove_qualifiers}
+@findex __remove_qualifiers
+
+@code{__remove_qualifiers} takes a typename as an argument:
+
+@smallexample
+__remove_qualifiers (const int)
+@end smallexample
+
+and produces the same type with all type qualifiers such as @code{const} and
+@code{volatile} removed.  In C11, the @code{_Atomic} qualifier is also removed
+so the resulting type is a non-atomic type.  This is useful in combination with
+@code{typeof}, e.g. for certain macros when passed const arguments:
+
+@smallexample
+#define MAX(x, y)                                \
+  (@{                                             \
+  __remove_qualifiers (__typeof (x)) ret = x;    \
+  if (y > ret) ret = y;                          \
+    ret;                                         \
+  @})
+
+const int ci = 5;
+MAX (ci, 12);
+@end smallexample
+
+In C++, it's possible to combine @code{__remove_qualifiers} with
+@code{decltype} instead of @code{typeof}.
+
+It is an error to pass an expression as an argument.
+
 @node Conditionals
 @section Conditionals with Omitted Operands
 @cindex conditional expressions, extensions
diff --git gcc/testsuite/c-c++-common/remove-quals-1.c gcc/testsuite/c-c++-common/remove-quals-1.c
index e69de29bb2d..28464eb6b87 100644
--- gcc/testsuite/c-c++-common/remove-quals-1.c
+++ gcc/testsuite/c-c++-common/remove-quals-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;
+
+  __remove_qualifiers (__typeof (i)) *nip = 0;
+  __remove_qualifiers (__typeof (ci)) *ncip = 0;
+  __remove_qualifiers (__typeof (vi)) *nvip = 0;
+
+  __remove_qualifiers (__typeof (i)) *nip2 = 0;
+  __remove_qualifiers (__typeof (ci)) *ncip2 = 0;
+  __remove_qualifiers (__typeof (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/remove-quals-2.c gcc/testsuite/c-c++-common/remove-quals-2.c
index e69de29bb2d..fac314d52d8 100644
--- gcc/testsuite/c-c++-common/remove-quals-2.c
+++ gcc/testsuite/c-c++-common/remove-quals-2.c
@@ -0,0 +1,32 @@
+/* PR c/65455 */
+/* { dg-do compile } */
+
+const int g(void);
+
+#define MAX(__x, __y)					\
+  ({							\
+  __remove_qualifiers (__typeof (__x)) __ret = __x;	\
+  if (__y > __ret) __ret = __y;				\
+    __ret;						\
+  })
+
+void
+fn (void)
+{
+  const int ci = 5;
+  __remove_qualifiers (__typeof (({ ci; }))) n1;
+  __remove_qualifiers (__typeof (ci)) n2;
+  __typeof (g ()) n4;
+  __remove_qualifiers (__typeof (g ())) n3;
+
+  typedef __remove_qualifiers (__typeof (ci)) T;
+  T n5;
+
+  n1 = 5;
+  n2 = 5;
+  n3 = 5;
+  n4 = 5;
+  n5 = 5;
+
+  MAX (ci, 12);
+}
diff --git gcc/testsuite/c-c++-common/remove-quals-3.c gcc/testsuite/c-c++-common/remove-quals-3.c
index e69de29bb2d..c3ace534adf 100644
--- gcc/testsuite/c-c++-common/remove-quals-3.c
+++ gcc/testsuite/c-c++-common/remove-quals-3.c
@@ -0,0 +1,17 @@
+/* PR c/65455 */
+/* { dg-do run } */
+
+int
+main ()
+{
+  __remove_qualifiers (const int) h;
+  h = 9;
+
+  int i = 1;
+  __typeof (int [++i]) e;
+  __remove_qualifiers (int [++i]) e2;
+  __remove_qualifiers (__typeof (int [++i])) e3;
+
+  if (i != 4)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/c-c++-common/remove-quals-4.c gcc/testsuite/c-c++-common/remove-quals-4.c
index e69de29bb2d..d140f553345 100644
--- gcc/testsuite/c-c++-common/remove-quals-4.c
+++ gcc/testsuite/c-c++-common/remove-quals-4.c
@@ -0,0 +1,15 @@
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wextra -Wpedantic -Wno-unused -Wno-vla" } */
+
+void
+fn (void)
+{
+  __typeof (int [1 / 0]) t1;
+  __remove_qualifiers (int [1 / 0]) t2;
+
+  int i;
+  __remove_qualifiers (i) t3; /* { dg-error ".__remove_qualifiers. can only be applied to a type|expected" } */
+  __remove_qualifiers (1) t4; /* { dg-error ".__remove_qualifiers. can only be applied to a type|expected" } */
+  __remove_qualifiers (int []) t5; /* { dg-error "array size|storage size" } */
+}
diff --git gcc/testsuite/g++.dg/ext/remove-quals-1.C gcc/testsuite/g++.dg/ext/remove-quals-1.C
index e69de29bb2d..0d0e33538b2 100644
--- gcc/testsuite/g++.dg/ext/remove-quals-1.C
+++ gcc/testsuite/g++.dg/ext/remove-quals-1.C
@@ -0,0 +1,12 @@
+// PR c/65455
+// { dg-do compile }
+// { dg-options "-pedantic-errors" }
+
+void
+fn (void)
+{
+  signed __remove_qualifiers (const int) s; /* { dg-error "used invalidly" } */
+
+  __typeof (struct S { int i; }) q; /* { dg-error "types may not be defined in .typeof. expressions" } */
+  __remove_qualifiers (struct S2 { int i; }) q2; /* { dg-error "types may not be defined in .__remove_qualifiers__. expressions" } */
+}
diff --git gcc/testsuite/g++.dg/ext/remove-quals-2.C gcc/testsuite/g++.dg/ext/remove-quals-2.C
index e69de29bb2d..10f7b5c5d7c 100644
--- gcc/testsuite/g++.dg/ext/remove-quals-2.C
+++ gcc/testsuite/g++.dg/ext/remove-quals-2.C
@@ -0,0 +1,25 @@
+// PR c/65455
+// { dg-do compile { target c++11 } }
+
+template<typename T, typename U>
+struct is_same
+{
+  static const bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T>
+{
+  static const bool value = true;
+};
+
+const int x = 1;
+typedef __remove_qualifiers (decltype (x)) T;
+T y = 1;
+volatile int i;
+static_assert(is_same<__remove_qualifiers (decltype (x)), int>::value,
+	      "type should be int");
+static_assert(is_same<decltype (y), int>::value,
+	      "type should be int");
+static_assert(is_same<__remove_qualifiers (decltype (i)), int>::value,
+	      "type should be int");
diff --git gcc/testsuite/gcc.dg/auto-type-3.c gcc/testsuite/gcc.dg/auto-type-3.c
index e69de29bb2d..79479c80084 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/remove-quals-1.c gcc/testsuite/gcc.dg/remove-quals-1.c
index e69de29bb2d..aecc17c949b 100644
--- gcc/testsuite/gcc.dg/remove-quals-1.c
+++ gcc/testsuite/gcc.dg/remove-quals-1.c
@@ -0,0 +1,23 @@
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "-Wrestrict" } */
+
+int *restrict t;
+
+void
+bar (__remove_qualifiers (__typeof (t)) p, __remove_qualifiers (__typeof (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" } */
+}
diff --git gcc/testsuite/gcc.dg/remove-quals-2.c gcc/testsuite/gcc.dg/remove-quals-2.c
index e69de29bb2d..2d13bf2b34e 100644
--- gcc/testsuite/gcc.dg/remove-quals-2.c
+++ gcc/testsuite/gcc.dg/remove-quals-2.c
@@ -0,0 +1,12 @@
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "-Wc++-compat" } */
+
+void
+fn (void)
+{
+  signed __remove_qualifiers (const int) s; /* { dg-error "two or more data types in declaration specifiers" } */
+
+  __typeof (struct S { int i; }) q; /* { dg-warning "defining type in .typeof. expression is invalid" } */
+  __remove_qualifiers (struct S2 { int i; }) q2; /* { dg-warning "defining type in .__remove_qualifiers. expression is invalid" } */
+}
diff --git gcc/testsuite/gcc.dg/remove-quals-3.c gcc/testsuite/gcc.dg/remove-quals-3.c
index e69de29bb2d..8e88031003d 100644
--- gcc/testsuite/gcc.dg/remove-quals-3.c
+++ gcc/testsuite/gcc.dg/remove-quals-3.c
@@ -0,0 +1,13 @@
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void
+fn (void)
+{
+  _Atomic int a;
+  __remove_qualifiers (_Atomic int) a; /* { dg-error "conflicting type qualifiers" } */
+
+  _Atomic int b;
+  __remove_qualifiers (__typeof (b)) b; /* { dg-error "conflicting type qualifiers" } */
+}

	Marek
Jason Merrill Sept. 9, 2017, 1:55 p.m. UTC | #3
On Fri, Sep 1, 2017 at 2:46 PM, Marek Polacek <polacek@redhat.com> wrote:
> @@ -16960,6 +16961,24 @@ cp_parser_simple_type_specifier (cp_parser* parser,
>
>        return type;
>
> +    case RID_REMOVE_QUALS:
> +      /* Consume the `__remove_qualifiers' token.  */
> +      cp_lexer_consume_token (parser->lexer);
> +      /* Parse the operand to __remove_qualifiers`'.  */
> +      type = cp_parser_sizeof_operand (parser, RID_REMOVE_QUALS);

Wouldn't it be simpler to use cp_parser_type_id?

> +      if (!TYPE_P (type))
> +       {
> +         error_at (token->location,
> +                   "%<__remove_qualifiers%> can only be applied to a type");
> +         type = error_mark_node;
> +       }

...rather than check this.

It doesn't look like you handle use of __remove_qualifiers in a
template, and you don't have any testcases for it.

> +@code{__remove_qualifiers} takes a typename as an argument:

I think it would be better to use the term "type-id" here, to avoid
confusion with "type-name", which is only a single identifier.

Jason
Joseph Myers Sept. 13, 2017, 4:36 p.m. UTC | #4
On Sat, 9 Sep 2017, Jason Merrill wrote:

> > +@code{__remove_qualifiers} takes a typename as an argument:
> 
> I think it would be better to use the term "type-id" here, to avoid
> confusion with "type-name", which is only a single identifier.

There's no such thing as a type-id in C, and type-name is the term used in 
C syntax (for what goes in a cast, etc.).
Joseph Myers Sept. 13, 2017, 4:54 p.m. UTC | #5
This patch (for C) is setting c_inhibit_evaluation_warnings and 
in_remove_qualifiers and doing corresponding use of pop_maybe_used, but I 
don't see the need for that.  Expressions within the argument to 
__remove_qualifiers are evaluated exactly if they would be evaluated in 
the containing context; it's not like sizeof or typeof (expression) which 
can cause expressions not to be evaluated.  (I don't actually think any of 
that handling is needed for typeof (type) either, it simply happens that 
the initial adjustments of c_inhibit_evaluation_warnings and in_typeof are 
done in code shared by typeof (expression) and typeof (type).)
diff mbox series

Patch

diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index d959dbc25bb..ae92ff440f6 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -423,6 +423,8 @@  const struct c_common_resword c_common_reswords[] =
   { "__null",		RID_NULL,	0 },
   { "__real",		RID_REALPART,	0 },
   { "__real__",		RID_REALPART,	0 },
+  { "__remove_qualifiers", RID_REMOVE_QUALS, 0 },
+  { "__remove_qualifiers__", RID_REMOVE_QUALS, 0 },
   { "__restrict",	RID_RESTRICT,	0 },
   { "__restrict__",	RID_RESTRICT,	0 },
   { "__signed",		RID_SIGNED,	0 },
@@ -7525,6 +7527,7 @@  keyword_begins_type_specifier (enum rid keyword)
     case RID_CLASS:
     case RID_UNION:
     case RID_ENUM:
+    case RID_REMOVE_QUALS:
       return true;
     default:
       if (keyword >= RID_FIRST_INT_N
diff --git gcc/c-family/c-common.h gcc/c-family/c-common.h
index 8e367680600..e726aa8844b 100644
--- gcc/c-family/c-common.h
+++ gcc/c-family/c-common.h
@@ -101,7 +101,7 @@  enum rid
   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_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,  RID_REMOVE_QUALS,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
   RID_FLOAT16,
diff --git gcc/c/c-decl.c gcc/c/c-decl.c
index d526f0e88e4..b9cd5f8cf56 100644
--- gcc/c/c-decl.c
+++ gcc/c/c-decl.c
@@ -7516,12 +7516,14 @@  start_struct (location_t loc, enum tree_code code, tree name,
      within a statement expr used within sizeof, et. al.  This is not
      terribly serious as C++ doesn't permit statement exprs within
      sizeof anyhow.  */
-  if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof))
+  if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof
+			  || in_remove_qualifiers))
     warning_at (loc, OPT_Wc___compat,
 		"defining type in %qs expression is invalid in C++",
-		(in_sizeof
-		 ? "sizeof"
-		 : (in_typeof ? "typeof" : "alignof")));
+		(in_sizeof ? "sizeof"
+		 : (in_typeof ? "typeof"
+			      : (in_alignof ? "alignof"
+					    : "__remove_qualifiers"))));
 
   return ref;
 }
@@ -8159,7 +8161,7 @@  finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
      struct_types.  */
   if (warn_cxx_compat
       && struct_parse_info != NULL
-      && !in_sizeof && !in_typeof && !in_alignof)
+      && !in_sizeof && !in_typeof && !in_alignof && !in_remove_qualifiers)
     struct_parse_info->struct_types.safe_push (t);
 
   return t;
@@ -8235,12 +8237,14 @@  start_enum (location_t loc, struct c_enum_contents *the_enum, tree name)
   /* FIXME: This will issue a warning for a use of a type defined
      within sizeof in a statement expr.  This is not terribly serious
      as C++ doesn't permit statement exprs within sizeof anyhow.  */
-  if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof))
+  if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof
+			  || in_remove_qualifiers))
     warning_at (loc, OPT_Wc___compat,
 		"defining type in %qs expression is invalid in C++",
-		(in_sizeof
-		 ? "sizeof"
-		 : (in_typeof ? "typeof" : "alignof")));
+		(in_sizeof ? "sizeof"
+		 : (in_typeof ? "typeof"
+			      : (in_alignof ? "alignof"
+					    : "__remove_qualifiers"))));
 
   return enumtype;
 }
@@ -8395,7 +8399,7 @@  finish_enum (tree enumtype, tree values, tree attributes)
      struct_types.  */
   if (warn_cxx_compat
       && struct_parse_info != NULL
-      && !in_sizeof && !in_typeof && !in_alignof)
+      && !in_sizeof && !in_typeof && !in_alignof && !in_remove_qualifiers)
     struct_parse_info->struct_types.safe_push (enumtype);
 
   return enumtype;
diff --git gcc/c/c-parser.c gcc/c/c-parser.c
index 3d15eb7a6de..048574b0139 100644
--- gcc/c/c-parser.c
+++ gcc/c/c-parser.c
@@ -504,6 +504,7 @@  c_keyword_starts_typename (enum rid keyword)
     case RID_ACCUM:
     case RID_SAT:
     case RID_AUTO_TYPE:
+    case RID_REMOVE_QUALS:
       return true;
     default:
       if (keyword >= RID_FIRST_INT_N
@@ -681,6 +682,7 @@  c_token_starts_declspecs (c_token *token)
 	case RID_ALIGNAS:
 	case RID_ATOMIC:
 	case RID_AUTO_TYPE:
+	case RID_REMOVE_QUALS:
 	  return true;
 	default:
 	  if (token->keyword >= RID_FIRST_INT_N
@@ -1361,6 +1363,7 @@  static struct c_typespec c_parser_enum_specifier (c_parser *);
 static struct c_typespec c_parser_struct_or_union_specifier (c_parser *);
 static tree c_parser_struct_declaration (c_parser *);
 static struct c_typespec c_parser_typeof_specifier (c_parser *);
+static struct c_typespec c_parser_remove_qualifiers_specifier (c_parser *);
 static tree c_parser_alignas_specifier (c_parser *);
 static struct c_declarator *c_parser_direct_declarator (c_parser *, bool,
 							c_dtr_syn, bool *);
@@ -2039,8 +2042,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))
+		  /* Remove all qualifiers from all types.  */
+		  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,
@@ -2732,6 +2735,14 @@  c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 	  t = c_parser_typeof_specifier (parser);
 	  declspecs_add_type (loc, specs, t);
 	  break;
+	case RID_REMOVE_QUALS:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_remove_qualifiers_specifier (parser);
+	  declspecs_add_type (loc, specs, t);
+	  break;
 	case RID_ATOMIC:
 	  /* C parser handling of Objective-C constructs needs
 	     checking for correct lvalue-to-rvalue conversions, and
@@ -3435,6 +3446,55 @@  c_parser_typeof_specifier (c_parser *parser)
   return ret;
 }
 
+/* Parse a __remove_qualifiers specifier (a GNU extension).
+
+   remove-qualifiers-specifier:
+     __remove_qualifiers ( type-name )
+*/
+
+static struct c_typespec
+c_parser_remove_qualifiers_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  ret.kind = ctsk_typeof;
+  ret.spec = error_mark_node;
+  ret.expr = NULL_TREE;
+  ret.expr_const_operands = true;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_REMOVE_QUALS));
+
+  /* Consume '__remove_qualifiers'.  */
+  c_parser_consume_token (parser);
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    return ret;
+
+  /* Only accept a type-name as an argument.  */
+  if (c_parser_next_tokens_start_typename (parser, cla_prefer_id))
+    {
+      /* Do not warn about problems with the expression.  */
+      c_inhibit_evaluation_warnings++;
+      in_remove_qualifiers++;
+      struct c_type_name *type = c_parser_type_name (parser);
+      /* Go back to evaluating expressions.  */
+      c_inhibit_evaluation_warnings--;
+      in_remove_qualifiers--;
+      if (type != NULL)
+	{
+	  ret.spec = groktypename (type, &ret.expr, &ret.expr_const_operands);
+	  pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE));
+	  /* Remove all type qualifiers.  */
+	  if (ret.spec != error_mark_node)
+	    ret.spec = c_build_qualified_type (ret.spec, TYPE_UNQUALIFIED);
+	}
+    }
+  else
+    error_at (c_parser_peek_token (parser)->location,
+	      "%<__remove_qualifiers%> can only be applied to a type");
+  parens.skip_until_found_close (parser);
+  return ret;
+}
+
 /* Parse an alignment-specifier.
 
    C11 6.7.5:
@@ -9879,7 +9939,7 @@  c_parser_objc_synchronized_statement (c_parser *parser)
        break continue return goto asm sizeof typeof __alignof
        unsigned long const short volatile signed restrict _Complex
        in out inout bycopy byref oneway int char float double void _Bool
-       _Atomic
+       _Atomic __remove_qualifiers
 
    ??? Why this selection of keywords but not, for example, storage
    class specifiers?  */
@@ -9944,6 +10004,7 @@  c_parser_objc_selector (c_parser *parser)
     case RID_INT_N_1:
     case RID_INT_N_2:
     case RID_INT_N_3:
+    case RID_REMOVE_QUALS:
       c_parser_consume_token (parser);
       return value;
     default:
diff --git gcc/c/c-tree.h gcc/c/c-tree.h
index 96c7ae7613f..e16c4185350 100644
--- gcc/c/c-tree.h
+++ gcc/c/c-tree.h
@@ -191,7 +191,7 @@  enum c_typespec_kind {
   ctsk_typedef,
   /* An ObjC-specific kind of type specifier.  */
   ctsk_objc,
-  /* A typeof specifier, or _Atomic ( type-name ).  */
+  /* A typeof specifier, _Atomic ( type-name ), or __remove_qualifiers.  */
   ctsk_typeof
 };
 
@@ -617,6 +617,7 @@  extern bool c_vla_unspec_p (tree x, tree fn);
 extern int in_alignof;
 extern int in_sizeof;
 extern int in_typeof;
+extern int in_remove_qualifiers;
 
 extern tree c_last_sizeof_arg;
 extern location_t c_last_sizeof_loc;
diff --git gcc/c/c-typeck.c gcc/c/c-typeck.c
index 135dd9d665c..ce720802e70 100644
--- gcc/c/c-typeck.c
+++ gcc/c/c-typeck.c
@@ -72,6 +72,9 @@  int in_sizeof;
 /* The level of nesting inside "typeof".  */
 int in_typeof;
 
+/* The level of nesting inside "__remove_qualifiers".  */
+int in_remove_qualifiers;
+
 /* The argument of last parsed sizeof expression, only to be tested
    if expr.original_code == SIZEOF_EXPR.  */
 tree c_last_sizeof_arg;
@@ -2782,7 +2785,7 @@  build_external_ref (location_t loc, tree id, bool fun, tree *type)
 
   if (TREE_CODE (ref) == FUNCTION_DECL && !in_alignof)
     {
-      if (!in_sizeof && !in_typeof)
+      if (!in_sizeof && !in_typeof && !in_remove_qualifiers)
 	C_DECL_USED (ref) = 1;
       else if (DECL_INITIAL (ref) == NULL_TREE
 	       && DECL_EXTERNAL (ref)
@@ -2838,7 +2841,7 @@  struct maybe_used_decl
 {
   /* The decl.  */
   tree decl;
-  /* The level seen at (in_sizeof + in_typeof).  */
+  /* The level seen at (in_sizeof + in_typeof + in_remove_qualifiers).  */
   int level;
   /* The next one at this level or above, or NULL.  */
   struct maybe_used_decl *next;
@@ -2856,7 +2859,7 @@  record_maybe_used_decl (tree decl)
 {
   struct maybe_used_decl *t = XOBNEW (&parser_obstack, struct maybe_used_decl);
   t->decl = decl;
-  t->level = in_sizeof + in_typeof;
+  t->level = in_sizeof + in_typeof + in_remove_qualifiers;
   t->next = maybe_used_decls;
   maybe_used_decls = t;
 }
@@ -2870,7 +2873,7 @@  void
 pop_maybe_used (bool used)
 {
   struct maybe_used_decl *p = maybe_used_decls;
-  int cur_level = in_sizeof + in_typeof;
+  int cur_level = in_sizeof + in_typeof + in_remove_qualifiers;
   while (p && p->level > cur_level)
     {
       if (used)
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 47d91bfa9a7..0e3d00dbc47 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -973,9 +973,10 @@  cp_keyword_starts_decl_specifier_p (enum rid keyword)
     case RID_FLOAT:
     case RID_DOUBLE:
     case RID_VOID:
-      /* GNU extensions.  */ 
+      /* GNU extensions.  */
     case RID_ATTRIBUTE:
     case RID_TYPEOF:
+    case RID_REMOVE_QUALS:
       /* C++0x extensions.  */
     case RID_DECLTYPE:
     case RID_UNDERLYING_TYPE:
@@ -16960,6 +16961,24 @@  cp_parser_simple_type_specifier (cp_parser* parser,
 
       return type;
 
+    case RID_REMOVE_QUALS:
+      /* Consume the `__remove_qualifiers' token.  */
+      cp_lexer_consume_token (parser->lexer);
+      /* Parse the operand to __remove_qualifiers`'.  */
+      type = cp_parser_sizeof_operand (parser, RID_REMOVE_QUALS);
+      if (!TYPE_P (type))
+	{
+	  error_at (token->location,
+		    "%<__remove_qualifiers%> can only be applied to a type");
+	  type = error_mark_node;
+	}
+      if (decl_specs)
+	cp_parser_set_decl_spec_type (decl_specs, type,
+				      token,
+				      /*type_definition_p=*/false);
+
+      return type;
+
     case RID_UNDERLYING_TYPE:
       type = cp_parser_trait_expr (parser, RID_UNDERLYING_TYPE);
       if (decl_specs)
@@ -27696,6 +27715,9 @@  cp_parser_sizeof_operand (cp_parser* parser, enum rid keyword)
 				 TYPENAME,
 				 /*initialized=*/0,
 				 /*attrlist=*/NULL);
+	  /* __remove_qualifiers removes all type qualifiers.  */
+	  if (keyword == RID_REMOVE_QUALS)
+	    expr = cp_build_qualified_type (expr, TYPE_UNQUALIFIED);
 	}
     }
 
diff --git gcc/doc/extend.texi gcc/doc/extend.texi
index 649be015dbb..749a9735f3b 100644
--- gcc/doc/extend.texi
+++ gcc/doc/extend.texi
@@ -29,6 +29,7 @@  extensions, accepted by GCC in C90 mode and in C++.
 * Nested Functions::    As in Algol and Pascal, lexical scoping of functions.
 * Constructing Calls::  Dispatching a call to another function.
 * Typeof::              @code{typeof}: referring to the type of an expression.
+* __remove_qualifiers::	Removing type qualifiers using @code{__remove_qualifiers}.
 * Conditionals::        Omitting the middle operand of a @samp{?:} expression.
 * __int128::		128-bit integers---@code{__int128}.
 * Long Long::           Double-word integers---@code{long long int}.
@@ -788,6 +789,37 @@  evaluated only once when using @code{__auto_type}, but twice if
 @code{typeof} is used.
 @end itemize
 
+@node __remove_qualifiers
+@section Removing type qualifiers using @code{__remove_qualifiers}
+@findex __remove_qualifiers
+
+@code{__remove_qualifiers} takes a typename as an argument:
+
+@smallexample
+__remove_qualifiers (const int)
+@end smallexample
+
+and produces the same type with all type qualifiers such as @code{const} and
+@code{volatile} removed.  This is useful in combination with @code{typeof},
+e.g. for certain macros when passed const arguments:
+
+@smallexample
+#define MAX(x, y)                                \
+  (@{                                             \
+  __remove_qualifiers (__typeof (x)) ret = x;    \
+  if (y > ret) ret = y;                          \
+    ret;                                         \
+  @})
+
+const int ci = 5;
+MAX (ci, 12);
+@end smallexample
+
+In C++, it's possible to combine @code{__remove_qualifiers} with
+@code{decltype} instead of @code{typeof}.
+
+It is an error to pass an expression as an argument.
+
 @node Conditionals
 @section Conditionals with Omitted Operands
 @cindex conditional expressions, extensions
diff --git gcc/testsuite/c-c++-common/remove-quals-1.c gcc/testsuite/c-c++-common/remove-quals-1.c
index e69de29bb2d..28464eb6b87 100644
--- gcc/testsuite/c-c++-common/remove-quals-1.c
+++ gcc/testsuite/c-c++-common/remove-quals-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;
+
+  __remove_qualifiers (__typeof (i)) *nip = 0;
+  __remove_qualifiers (__typeof (ci)) *ncip = 0;
+  __remove_qualifiers (__typeof (vi)) *nvip = 0;
+
+  __remove_qualifiers (__typeof (i)) *nip2 = 0;
+  __remove_qualifiers (__typeof (ci)) *ncip2 = 0;
+  __remove_qualifiers (__typeof (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/remove-quals-2.c gcc/testsuite/c-c++-common/remove-quals-2.c
index e69de29bb2d..fac314d52d8 100644
--- gcc/testsuite/c-c++-common/remove-quals-2.c
+++ gcc/testsuite/c-c++-common/remove-quals-2.c
@@ -0,0 +1,32 @@ 
+/* PR c/65455 */
+/* { dg-do compile } */
+
+const int g(void);
+
+#define MAX(__x, __y)					\
+  ({							\
+  __remove_qualifiers (__typeof (__x)) __ret = __x;	\
+  if (__y > __ret) __ret = __y;				\
+    __ret;						\
+  })
+
+void
+fn (void)
+{
+  const int ci = 5;
+  __remove_qualifiers (__typeof (({ ci; }))) n1;
+  __remove_qualifiers (__typeof (ci)) n2;
+  __typeof (g ()) n4;
+  __remove_qualifiers (__typeof (g ())) n3;
+
+  typedef __remove_qualifiers (__typeof (ci)) T;
+  T n5;
+
+  n1 = 5;
+  n2 = 5;
+  n3 = 5;
+  n4 = 5;
+  n5 = 5;
+
+  MAX (ci, 12);
+}
diff --git gcc/testsuite/c-c++-common/remove-quals-3.c gcc/testsuite/c-c++-common/remove-quals-3.c
index e69de29bb2d..c3ace534adf 100644
--- gcc/testsuite/c-c++-common/remove-quals-3.c
+++ gcc/testsuite/c-c++-common/remove-quals-3.c
@@ -0,0 +1,17 @@ 
+/* PR c/65455 */
+/* { dg-do run } */
+
+int
+main ()
+{
+  __remove_qualifiers (const int) h;
+  h = 9;
+
+  int i = 1;
+  __typeof (int [++i]) e;
+  __remove_qualifiers (int [++i]) e2;
+  __remove_qualifiers (__typeof (int [++i])) e3;
+
+  if (i != 4)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/c-c++-common/remove-quals-4.c gcc/testsuite/c-c++-common/remove-quals-4.c
index e69de29bb2d..d140f553345 100644
--- gcc/testsuite/c-c++-common/remove-quals-4.c
+++ gcc/testsuite/c-c++-common/remove-quals-4.c
@@ -0,0 +1,15 @@ 
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wextra -Wpedantic -Wno-unused -Wno-vla" } */
+
+void
+fn (void)
+{
+  __typeof (int [1 / 0]) t1;
+  __remove_qualifiers (int [1 / 0]) t2;
+
+  int i;
+  __remove_qualifiers (i) t3; /* { dg-error ".__remove_qualifiers. can only be applied to a type|expected" } */
+  __remove_qualifiers (1) t4; /* { dg-error ".__remove_qualifiers. can only be applied to a type|expected" } */
+  __remove_qualifiers (int []) t5; /* { dg-error "array size|storage size" } */
+}
diff --git gcc/testsuite/g++.dg/ext/remove-quals-1.C gcc/testsuite/g++.dg/ext/remove-quals-1.C
index e69de29bb2d..0d0e33538b2 100644
--- gcc/testsuite/g++.dg/ext/remove-quals-1.C
+++ gcc/testsuite/g++.dg/ext/remove-quals-1.C
@@ -0,0 +1,12 @@ 
+// PR c/65455
+// { dg-do compile }
+// { dg-options "-pedantic-errors" }
+
+void
+fn (void)
+{
+  signed __remove_qualifiers (const int) s; /* { dg-error "used invalidly" } */
+
+  __typeof (struct S { int i; }) q; /* { dg-error "types may not be defined in .typeof. expressions" } */
+  __remove_qualifiers (struct S2 { int i; }) q2; /* { dg-error "types may not be defined in .__remove_qualifiers__. expressions" } */
+}
diff --git gcc/testsuite/g++.dg/ext/remove-quals-2.C gcc/testsuite/g++.dg/ext/remove-quals-2.C
index e69de29bb2d..10f7b5c5d7c 100644
--- gcc/testsuite/g++.dg/ext/remove-quals-2.C
+++ gcc/testsuite/g++.dg/ext/remove-quals-2.C
@@ -0,0 +1,25 @@ 
+// PR c/65455
+// { dg-do compile { target c++11 } }
+
+template<typename T, typename U>
+struct is_same
+{
+  static const bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T>
+{
+  static const bool value = true;
+};
+
+const int x = 1;
+typedef __remove_qualifiers (decltype (x)) T;
+T y = 1;
+volatile int i;
+static_assert(is_same<__remove_qualifiers (decltype (x)), int>::value,
+	      "type should be int");
+static_assert(is_same<decltype (y), int>::value,
+	      "type should be int");
+static_assert(is_same<__remove_qualifiers (decltype (i)), int>::value,
+	      "type should be int");
diff --git gcc/testsuite/gcc.dg/auto-type-3.c gcc/testsuite/gcc.dg/auto-type-3.c
index e69de29bb2d..79479c80084 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/remove-quals-1.c gcc/testsuite/gcc.dg/remove-quals-1.c
index e69de29bb2d..aecc17c949b 100644
--- gcc/testsuite/gcc.dg/remove-quals-1.c
+++ gcc/testsuite/gcc.dg/remove-quals-1.c
@@ -0,0 +1,23 @@ 
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "-Wrestrict" } */
+
+int *restrict t;
+
+void
+bar (__remove_qualifiers (__typeof (t)) p, __remove_qualifiers (__typeof (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" } */
+}
diff --git gcc/testsuite/gcc.dg/remove-quals-2.c gcc/testsuite/gcc.dg/remove-quals-2.c
index e69de29bb2d..2d13bf2b34e 100644
--- gcc/testsuite/gcc.dg/remove-quals-2.c
+++ gcc/testsuite/gcc.dg/remove-quals-2.c
@@ -0,0 +1,12 @@ 
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "-Wc++-compat" } */
+
+void
+fn (void)
+{
+  signed __remove_qualifiers (const int) s; /* { dg-error "two or more data types in declaration specifiers" } */
+
+  __typeof (struct S { int i; }) q; /* { dg-warning "defining type in .typeof. expression is invalid" } */
+  __remove_qualifiers (struct S2 { int i; }) q2; /* { dg-warning "defining type in .__remove_qualifiers. expression is invalid" } */
+}