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