This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: C/C++ PATCH to add __remove_qualifiers (PR c/65455, c/39985)
- From: Marek Polacek <polacek at redhat dot com>
- To: Joseph Myers <joseph at codesourcery dot com>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>, Jason Merrill <jason at redhat dot com>
- Date: Fri, 1 Sep 2017 14:46:38 +0200
- Subject: Re: C/C++ PATCH to add __remove_qualifiers (PR c/65455, c/39985)
- Authentication-results: sourceware.org; auth=none
- Authentication-results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com
- Authentication-results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=polacek at redhat dot com
- Dmarc-filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 7315D404338
- References: <20170831152515.GR17069@redhat.com> <alpine.DEB.2.20.1708311637240.24662@digraph.polyomino.org.uk>
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