Affects versions down to r6, at -O1+ : (gcc configured with --enable-checking=yes) $ cat z1.c #include <stdarg.h> void foo (int size, ...) { struct {} d; va_list ap; d = va_arg (ap, typeof(d)()) (); } $ gcc-5 -c z1.c -O2 $ gcc-12-20220403 -c z1.c $ $ gcc-12-20220403 -c z1.c -O2 z1.c: In function 'foo': z1.c:7:1: error: virtual definition of statement not up to date 7 | } | ^ D.1987 = _1; during GIMPLE pass: ssa z1.c:7:1: internal compiler error: verify_ssa failed 0x10a33db verify_ssa(bool, bool) ../../gcc/tree-ssa.cc:1211 0xd01ea7 execute_function_todo ../../gcc/passes.cc:2092 0xd027f2 execute_todo ../../gcc/passes.cc:2139
Started with r6-91-gf8e89441bc5518f450b6511c59c17c837859d109
I will have a look.
The odd thing is that we parse d = va_arg (ap, typeof(d)()) (); as function call, gimplifying it as _1 = .VA_ARG (&ap(address-taken), 0B, 0B); D.1987(address-taken) = _1; D.1987(address-taken) (); <modify_expr 0x7ffff6650d70 type <record_type 0x7ffff66793f0 type_0 BLK size <integer_cst 0x7ffff6517dc8 constant 0> unit-size <integer_cst 0x7ffff6517d80 constant 0> align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7ffff66793f0 context <function_decl 0x7ffff6656200 foo> chain <type_decl 0x7ffff65415f0 D.1983>> side-effects arg:0 <var_decl 0x7ffff6526bd0 d type <record_type 0x7ffff66793f0> used read BLK t.c:4:13 size <integer_cst 0x7ffff6517dc8 0> unit-size <integer_cst 0x7ffff6517d80 0> align:8 warn_if_not_align:0 context <function_decl 0x7ffff6656200 foo> chain <var_decl 0x7ffff6526c60 ap type <array_type 0x7ffff66792a0 va_list> addressable used read BLK t.c:5:11 size <integer_cst 0x7ffff6537210 constant 192> unit-size <integer_cst 0x7ffff65371e0 constant 24> align:64 warn_if_not_align:0 context <function_decl 0x7ffff6656200 foo>>> arg:1 <call_expr 0x7ffff66786c0 type <record_type 0x7ffff66793f0> side-effects fn <addr_expr 0x7ffff66652a0 type <pointer_type 0x7ffff6679540> side-effects arg:0 <va_arg_expr 0x7ffff6665280 type <function_type 0x7ffff6679498> side-effects arg:0 <addr_expr 0x7ffff6665260 type <pointer_type 0x7ffff6542b28> arg:0 <var_decl 0x7ffff6526c60 ap>> t.c:6:7 start: t.c:6:7 finish: t.c:6:7> t.c:6:7 start: t.c:6:7 finish: t.c:6:7> t.c:6:7 start: t.c:6:7 finish: t.c:6:33> t.c:6:5 start: t.c:6:3 finish: t.c:6:33> I _think_ we fail to decay the function type in va_arg to a pointer type or we fail to reject this testcase. Somewhat reduced testcase: #include <stdarg.h> typedef struct {} D; void foo (int size, ...) { va_list ap; va_arg (ap, D()) (); } C17/7.16.1.1 isn't sufficiently clear as to whether this is valid, but at least "The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type" isn't fulfilled here since the corresponding pointer type would be D(*)(), not D()*. clang rejects this with t.c:6:15: error: second argument to 'va_arg' is of non-POD type 'D ()' [-Wnon-pod-varargs] va_arg (ap, D()) (); ^~~ but not sure if "POD" is a thing to use in C language diagnostics. Anyway, defering to C frontend maintainers.
It looks like c_build_va_arg could be amended to reject FUNCTION_TYPE type. But as said, not sure if this is valid or not.
The C++ frontend accepts this and diagnoses In file included from t.c:1: t.c: In function 'void foo(int, ...)': t.c:6:15: warning: 'D()' is promoted to 'D (*)()' when passed through '...' 6 | va_arg (ap, D()) (); and produces the following GENERIC, decaying the "loaded function": <call_expr 0x7ffff6692c00 type <record_type 0x7ffff6681348 D cxx-odr-p type_5 QI size <integer_cst 0x7ffff653a048 constant 8> unit-size <integer_cst 0x7ffff653a060 constant 1> align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7ffff6681348 fields <function_decl 0x7ffff6685600 __dt type <method_type 0x7ffff6698000> public abstract external autoinline decl_3 QI t.c:2:19 align:16 warn_if_not_align:0 context <record_type 0x7ffff6681348 D> full-name "D::~D() noexcept (<uninstantiated>)" not-really-extern chain <function_decl 0x7ffff6685800 __dt_base >> context <translation_unit_decl 0x7ffff6525168 t.c> full-name "struct D" X() X(constX&) this=(X&) n_parents=0 use_template=0 interface-unknown pointer_to_this <pointer_type 0x7ffff66817e0> chain <type_decl 0x7ffff6547980 ._anon_0>> side-effects fn <addr_expr 0x7ffff6678980 type <pointer_type 0x7ffff6681690 type <function_type 0x7ffff66815e8> unsigned DI size <integer_cst 0x7ffff6517f48 constant 64> unit-size <integer_cst 0x7ffff6517f60 constant 8> align:64 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7ffff6681690> side-effects arg:0 <va_arg_expr 0x7ffff6678960 type <function_type 0x7ffff66815e8> side-effects arg:0 <addr_expr 0x7ffff6678a00 type <pointer_type 0x7ffff6544dc8> arg:0 <var_decl 0x7ffff6526c60 ap>> t.c:6:3 start: t.c:6:3 finish: t.c:6:3> t.c:6:3 start: t.c:6:3 finish: t.c:6:3> t.c:6:3 start: t.c:6:3 finish: t.c:6:3> which we then gimplify to __builtin_trap (); <<< Unknown tree: integer_cst >>> ();
(In reply to Richard Biener from comment #5) > The C++ frontend accepts this and diagnoses > > In file included from t.c:1: > t.c: In function 'void foo(int, ...)': > t.c:6:15: warning: 'D()' is promoted to 'D (*)()' when passed through '...' > 6 | va_arg (ap, D()) (); > > and produces the following GENERIC, decaying the "loaded function": include/stdarg.h:49:44: note: in definition of macro 'va_arg' 49 | #define va_arg(v,l) __builtin_va_arg(v,l) | ^ t.c:6:15: note: (so you should pass 'D (*)()' not 'D()' to 'va_arg') 6 | va_arg (ap, D()) (); include/stdarg.h:49:44: note: in definition of macro 'va_arg' 49 | #define va_arg(v,l) __builtin_va_arg(v,l) | ^ t.c:6:15: note: if this code is reached, the program will abort 6 | va_arg (ap, D()) (); include/stdarg.h:49:44: note: in definition of macro 'va_arg' 49 | #define va_arg(v,l) __builtin_va_arg(v,l) | ^ because the C++ implementation of lang_hooks.types.type_promotes_to says the function type doesn't promote to itself. The C frontend seems to lack that. > > > <call_expr 0x7ffff6692c00 > type <record_type 0x7ffff6681348 D cxx-odr-p type_5 QI > size <integer_cst 0x7ffff653a048 constant 8> > unit-size <integer_cst 0x7ffff653a060 constant 1> > align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type > 0x7ffff6681348 > fields <function_decl 0x7ffff6685600 __dt type <method_type > 0x7ffff6698000> > public abstract external autoinline decl_3 QI t.c:2:19 align:16 > warn_if_not_align:0 context <record_type 0x7ffff6681348 D> > full-name "D::~D() noexcept (<uninstantiated>)" > not-really-extern chain <function_decl 0x7ffff6685800 __dt_base > >> context <translation_unit_decl 0x7ffff6525168 t.c> > full-name "struct D" > X() X(constX&) this=(X&) n_parents=0 use_template=0 interface-unknown > pointer_to_this <pointer_type 0x7ffff66817e0> chain <type_decl > 0x7ffff6547980 ._anon_0>> > side-effects > fn <addr_expr 0x7ffff6678980 > type <pointer_type 0x7ffff6681690 type <function_type 0x7ffff66815e8> > unsigned DI > size <integer_cst 0x7ffff6517f48 constant 64> > unit-size <integer_cst 0x7ffff6517f60 constant 8> > align:64 warn_if_not_align:0 symtab:0 alias-set -1 > canonical-type 0x7ffff6681690> > side-effects > arg:0 <va_arg_expr 0x7ffff6678960 type <function_type 0x7ffff66815e8> > side-effects > arg:0 <addr_expr 0x7ffff6678a00 type <pointer_type > 0x7ffff6544dc8> > arg:0 <var_decl 0x7ffff6526c60 ap>> > t.c:6:3 start: t.c:6:3 finish: t.c:6:3> > t.c:6:3 start: t.c:6:3 finish: t.c:6:3> > t.c:6:3 start: t.c:6:3 finish: t.c:6:3> > > which we then gimplify to > > __builtin_trap (); > <<< Unknown tree: integer_cst >>> ();
I think it's valid to reject this at compile time (rather than just generating a runtime trap): the "such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type" can never be satisfied for a function type, even if e.g. a typedef name is used so that postfixing '*' produces valid syntax for the corresponding pointer type, because it still wouldn't be "the type of a pointer to an object".
Created attachment 52767 [details] gcc12-pr105149.patch Untested patch to reject it.
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:6e2743687202c58a6553ae632ebbada3de38ad48 commit r12-8057-g6e2743687202c58a6553ae632ebbada3de38ad48 Author: Jakub Jelinek <jakub@redhat.com> Date: Fri Apr 8 09:16:30 2022 +0200 c: Error on va_arg with function type [PR105149] In the PR Joseph said that the C standard for va_arg talks about pointers to object type and as a function type is not object type, it is invalid. The following patch diagnoses it in the FE, instead of ICEing later on when optimizations are turned on (and with -O0 doing something weird at runtime). 2022-04-08 Jakub Jelinek <jakub@redhat.com> PR c/105149 * c-typeck.cc (c_build_va_arg): Reject function types. * gcc.dg/pr105149.c: New test.
Fixed on the trunk. Unsure about backports though, if we do something for those, might be better to silently turn it into __builtin_trap() than to reject it when it wasn't rejected before.
GCC 9 branch is being closed
GCC 10.4 is being released, retargeting bugs to GCC 10.5.
GCC 10 branch is being closed.