This patch adds a variant of __typeof, called __typeof_noqual.  As the name
suggests, this variant always drops all qualifiers, not just when the type
is atomic.  This was discussed several times in the past, see e.g.
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39985>
or
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65455>
It's been brought to my attention again here:
<https://gcc.gnu.org/ml/gcc-patches/2017-05/msg01955.html>

One approach would be to just modify the current __typeof, but that could
cause some incompatibilities, I'm afraid.  This is based on rth's earlier
patch: <https://gcc.gnu.org/ml/gcc-patches/2016-02/msg00268.html> but I
didn't do the address space-stripping variant __typeof_noas.  I also added
a couple of missing things.

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.

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

2017-06-23  Marek Polacek  <pola...@redhat.com>
            Richard Henderson  <r...@redhat.com>

        PR c/65455
        PR c/39985
        * c-common.c (c_common_reswords): Add __typeof_noqual,
        __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.
        * doc/invoke.texi: Update documentation regarding typeof.

        * 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.


diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index f6a9d05..db9c3ba 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 },
@@ -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 },
@@ -7511,6 +7514,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 1748c19..3d98697 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 6f954f2..9899592 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:
@@ -1873,8 +1875,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,
@@ -2555,6 +2557,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?  */
@@ -3218,7 +3221,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++;
@@ -3260,7 +3266,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 %<)%>");
@@ -9709,6 +9717,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 ddb1cf3..830b5b0 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:
@@ -16834,6 +16835,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'.  */
@@ -16841,6 +16843,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..b323ecd 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/doc/invoke.texi gcc/doc/invoke.texi
index 7c81f0d..de1ff3d 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -1738,17 +1738,17 @@ equivalent to @option{-std=c++98}.
 
 This turns off certain features of GCC that are incompatible with ISO
 C90 (when compiling C code), or of standard C++ (when compiling C++ code),
-such as the @code{asm} and @code{typeof} keywords, and
+such as the @code{asm}, @code{typeof}, and @code{typeof_noqual} keywords, and
 predefined macros such as @code{unix} and @code{vax} that identify the
 type of system you are using.  It also enables the undesirable and
 rarely used ISO trigraph feature.  For the C compiler,
 it disables recognition of C++ style @samp{//} comments as well as
 the @code{inline} keyword.
 
-The alternate keywords @code{__asm__}, @code{__extension__},
-@code{__inline__} and @code{__typeof__} continue to work despite
-@option{-ansi}.  You would not want to use them in an ISO C program, of
-course, but it is useful to put them in header files that might be included
+The alternate keywords @code{__asm__}, @code{__extension__}, @code{__inline__},
+@code{__typeof__}, and @code{__typeof_noqual__} continue to work despite
+@option{-ansi}.  You would not want to use them in an ISO C program, of course,
+but it is useful to put them in header files that might be included
 in compilations done with @option{-ansi}.  Alternate predefined macros
 such as @code{__unix__} and @code{__vax__} are also available, with or
 without @option{-ansi}.
@@ -1949,17 +1949,18 @@ supported for C as this construct is allowed by C++.
 
 @item -fno-asm
 @opindex fno-asm
-Do not recognize @code{asm}, @code{inline} or @code{typeof} as a
-keyword, so that code can use these words as identifiers.  You can use
-the keywords @code{__asm__}, @code{__inline__} and @code{__typeof__}
-instead.  @option{-ansi} implies @option{-fno-asm}.
-
-In C++, this switch only affects the @code{typeof} keyword, since
-@code{asm} and @code{inline} are standard keywords.  You may want to
-use the @option{-fno-gnu-keywords} flag instead, which has the same
+Do not recognize @code{asm}, @code{inline}, @code{typeof} or
+@code{typeof_noqual} as a keyword, so that code can use these words as
+identifiers.  You can use the keywords @code{__asm__}, @code{__inline__},
+@code{__typeof__}, and @code{__typeof_noqual__} instead.  @option{-ansi}
+implies @option{-fno-asm}.
+
+In C++, this switch only affects the @code{typeof} and @code{typeof_noqual}
+keywords , since @code{asm} and @code{inline} are standard keywords.  You may
+want to use the @option{-fno-gnu-keywords} flag instead, which has the same
 effect.  In C99 mode (@option{-std=c99} or @option{-std=gnu99}), this
-switch only affects the @code{asm} and @code{typeof} keywords, since
-@code{inline} is a standard keyword in ISO C99.
+switch only affects the @code{asm}, @code{typeof}, and @code{typeof_noqual}
+keywords, since @code{inline} is a standard keyword in ISO C99.
 
 @item -fno-builtin
 @itemx -fno-builtin-@var{function}
@@ -2436,8 +2437,9 @@ otherwise be invalid, or have different behavior.
 
 @item -fno-gnu-keywords
 @opindex fno-gnu-keywords
-Do not recognize @code{typeof} as a keyword, so that code can use this
-word as an identifier.  You can use the keyword @code{__typeof__} instead.
+Do not recognize @code{typeof} and @code{typeof_noqual }as a keyword, so that
+code can use this word as an identifier.  You can use the keyword
+@code{__typeof__} or @code{__typeof_noqual__} instead.
 This option is implied by the strict ISO C++ dialects: @option{-ansi},
 @option{-std=c++98}, @option{-std=c++11}, etc.
 
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..c1c07d5 100644
--- gcc/testsuite/c-c++-common/typeof-noqual-2.c
+++ gcc/testsuite/c-c++-common/typeof-noqual-2.c
@@ -0,0 +1,35 @@
+/* PR c/65455 */
+/* { dg-do compile } */
+/* { dg-options "-fgnu-keywords" { target c++ } } */
+
+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;
+  typeof_noqual (ci) n6;
+
+  typedef __typeof_noqual(ci) T;
+  T n5;
+
+  n1 = 5;
+  n2 = 5;
+  n3 = 5;
+  n4 = 5;
+  n5 = 5;
+  n6 = 5;
+
+  MAX (ci, 12);
+}
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" } */
+}

        Marek

Reply via email to