@@ -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:
@@ -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,
@@ -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:
@@ -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,
@@ -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
@@ -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;
+}
@@ -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);
+}
@@ -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++;
+}
@@ -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" } */
+}