Hi! Here is an untested patch that should fix all of that, ok for trunk if it passes bootstrap/regtest on {x86_64,i686}-linux?
Notes: 1) seems the C++ FE is even stricter and uses the builtin_structptr_types[x].str string to verify the pointed type has the right TYPE_NAME; is that something we want to require for C also? That would mean we'd warn about Wbuiltin-declaration-mismatch-6.c etc. too, because it uses: struct StdioFile; int fprintf (struct StdioFile*, const char*, ...); If it was typedef struct StdioFile FILE; int fprintf (FILE*, const char*, ...); that would be accepted then. Also, unlike C++, C has struct tags vs. typedef tags as separate namespaces, so we'd need to either handle it specially for struct tm (where the standard prototype is const struct tm *, not const tm *) vs. the rest, or accept struct as well as typedef names equal to builtin_structptr_types[x].str, or don't do anything here (I didn't want to be even stricter than Martin's patch) 2) it accepts any kind of pointed type and just requires it is the same later on, so it accepts e.g. struct StdioFile; typedef struct StdioFile FILE; int fprintf (const FILE *, const char *, ...); and then requires those pointers to be the same in all cases. Shall we verify the qualifiers on the pointed type before we accept it into last_structptr_types? If yes, right now it would emit weird diagnostics (request that the type would be say const void * instead of struct tm *) 3) for fexcept_t and fenv_t, there are actually 2 types for each, const {fexcept,fenv}_t * vs. {fexcept,fenv}_t *. No verification is done that the TYPE_MAIN_VARIANT is the same between both if both are seen. This only makes sense if 2) is implemented, and would probably work if both 1) and 2) are implemented without anything further. Not planning to work on these myself, all I want to fix is the breakage. 2019-01-26 Jakub Jelinek <ja...@redhat.com> PR c/86125 * c-decl.c (last_fileptr_type): Remove. (last_structptr_types): New variable. (match_builtin_function_types): Compare TYPE_MAIN_VARIANT of {old,new}rettype instead of the types themselves. Assert last_structptr_types array has the same number of elements as builtin_structptr_types array. Use TYPE_MAIN_VARIANT for argument oldtype and newtype. Instead of handling just fileptr_type_node specially, handle all builtin_structptr_types pointer nodes. Formatting fix. * c-common.c (c_common_nodes_and_builtins): Build type variants for builtin_structptr_types types even for C. * gcc.dg/Wbuiltin-declaration-mismatch-7.c: Guard testcase for lp64, ilp32 and llp64 only. (fputs): Use unsigned long long instead of size_t for return type. (vfprintf, vfscanf): Accept arbitrary target specific type for va_list. --- gcc/c/c-decl.c.jj 2019-01-25 23:46:06.121198286 +0100 +++ gcc/c/c-decl.c 2019-01-26 15:34:02.749450800 +0100 @@ -1632,13 +1632,13 @@ c_bind (location_t loc, tree decl, bool } -/* Stores the first FILE* argument type (whatever it is) seen in - a declaration of a file I/O built-in. Subsequent declarations - of such built-ins are expected to refer to it rather than to - fileptr_type_node which is just void* (or to any other type). +/* Stores the first FILE*, const struct tm* etc. argument type (whatever it + is) seen in a declaration of a file I/O etc. built-in. Subsequent + declarations of such built-ins are expected to refer to it rather than to + fileptr_type_node etc. which is just void* (or to any other type). Used only by match_builtin_function_types. */ -static GTY(()) tree last_fileptr_type; +static GTY(()) tree last_structptr_types[6]; /* Subroutine of compare_decls. Allow harmless mismatches in return and argument types provided that the type modes match. Set *STRICT @@ -1660,13 +1660,18 @@ match_builtin_function_types (tree newty if (TYPE_MODE (oldrettype) != TYPE_MODE (newrettype)) return NULL_TREE; - if (!comptypes (oldrettype, newrettype)) + if (!comptypes (TYPE_MAIN_VARIANT (oldrettype), + TYPE_MAIN_VARIANT (newrettype))) *strict = oldrettype; tree oldargs = TYPE_ARG_TYPES (oldtype); tree newargs = TYPE_ARG_TYPES (newtype); tree tryargs = newargs; + gcc_checking_assert ((sizeof (last_structptr_types) + / sizeof (last_structptr_types[0])) + == (sizeof (builtin_structptr_types) + / sizeof (builtin_structptr_types[0]))); for (unsigned i = 1; oldargs || newargs; ++i) { if (!oldargs @@ -1675,8 +1680,8 @@ match_builtin_function_types (tree newty || !TREE_VALUE (newargs)) return NULL_TREE; - tree oldtype = TREE_VALUE (oldargs); - tree newtype = TREE_VALUE (newargs); + tree oldtype = TYPE_MAIN_VARIANT (TREE_VALUE (oldargs)); + tree newtype = TYPE_MAIN_VARIANT (TREE_VALUE (newargs)); /* Fail for types with incompatible modes/sizes. */ if (TYPE_MODE (TREE_VALUE (oldargs)) @@ -1684,28 +1689,39 @@ match_builtin_function_types (tree newty return NULL_TREE; /* Fail for function and object pointer mismatches. */ - if (FUNCTION_POINTER_TYPE_P (oldtype) != FUNCTION_POINTER_TYPE_P (newtype) + if ((FUNCTION_POINTER_TYPE_P (oldtype) + != FUNCTION_POINTER_TYPE_P (newtype)) || POINTER_TYPE_P (oldtype) != POINTER_TYPE_P (newtype)) return NULL_TREE; - if (oldtype == fileptr_type_node) - { - /* Store the first FILE* argument type (whatever it is), and - expect any subsequent declarations of file I/O built-ins - to refer to it rather than to fileptr_type_node which is - just void*. */ - if (last_fileptr_type) - { - if (!comptypes (last_fileptr_type, newtype)) - { - *argno = i; - *strict = last_fileptr_type; - } - } - else - last_fileptr_type = newtype; - } - else if (!*strict && !comptypes (oldtype, newtype)) + unsigned j = (sizeof (builtin_structptr_types) + / sizeof (builtin_structptr_types[0])); + if (POINTER_TYPE_P (oldtype)) + for (j = 0; j < (sizeof (builtin_structptr_types) + / sizeof (builtin_structptr_types[0])); ++j) + { + if (TREE_VALUE (oldargs) != builtin_structptr_types[j].node) + continue; + /* Store the first FILE* etc. argument type (whatever it is), and + expect any subsequent declarations of file I/O etc. built-ins + to refer to it rather than to fileptr_type_node etc. which is + just void* (or const void*). */ + if (last_structptr_types[j]) + { + if (!comptypes (last_structptr_types[j], newtype)) + { + *argno = i; + *strict = last_structptr_types[j]; + } + } + else + last_structptr_types[j] = newtype; + break; + } + if (j == (sizeof (builtin_structptr_types) + / sizeof (builtin_structptr_types[0])) + && !*strict + && !comptypes (oldtype, newtype)) { *argno = i; *strict = oldtype; --- gcc/c-family/c-common.c.jj 2019-01-16 09:35:04.566323056 +0100 +++ gcc/c-family/c-common.c 2019-01-26 14:39:04.909695084 +0100 @@ -4296,18 +4296,13 @@ c_common_nodes_and_builtins (void) COMPLEX_FLOATN_NX_TYPE_NODE (i))); } - if (c_dialect_cxx ()) - { - /* For C++, make fileptr_type_node a distinct void * type until - FILE type is defined. Likewise for const struct tm*. */ - for (unsigned i = 0; - i < sizeof (builtin_structptr_types) - / sizeof (builtin_structptr_type); - ++i) - builtin_structptr_types[i].node = - build_variant_type_copy (builtin_structptr_types[i].base); - - } + /* Make fileptr_type_node a distinct void * type until + FILE type is defined. Likewise for const struct tm*. */ + for (unsigned i = 0; + i < sizeof (builtin_structptr_types) / sizeof (builtin_structptr_type); + ++i) + builtin_structptr_types[i].node + = build_variant_type_copy (builtin_structptr_types[i].base); record_builtin_type (RID_VOID, NULL, void_type_node); --- gcc/testsuite/gcc.dg/Wbuiltin-declaration-mismatch-7.c.jj 2019-01-25 06:08:50.629941283 +0100 +++ gcc/testsuite/gcc.dg/Wbuiltin-declaration-mismatch-7.c 2019-01-26 13:23:18.126323403 +0100 @@ -2,7 +2,7 @@ return type Verify that a declaration of vfprintf() with withe the wrong last argument triggers -Wbuiltin-declaration-mismatch even without -Wextra. - { dg-do compile } + { dg-do compile { target { lp64 || ilp32 || llp64 } } } { dg-options "-Wbuiltin-declaration-mismatch" } */ struct StdioFile; @@ -13,14 +13,14 @@ struct StdioFile; int fprintf (struct StdioFile*, const char*); /* { dg-warning "conflicting types for built-in function .fprintf.; expected .int\\\(\[a-z_\]+ \\\*, const char \\\*, \.\.\.\\\)." } */ -int vfprintf (struct StdioFile*, const char*, ...); /* { dg-warning "conflicting types for built-in function .vfprintf.; expected .int\\\(\[a-z_\]+ \\\*, const char \\\*, \[a-z_\]+ \\\*\\\)." } */ +int vfprintf (struct StdioFile*, const char*, ...); /* { dg-warning "conflicting types for built-in function .vfprintf.; expected .int\\\(\[a-z_\]+ \\\*, const char \\\*, \[^\n\r,\\\)\]+\\\)." } */ int fputc (char, struct StdioFile*); /* { dg-warning "conflicting types for built-in function .fputc.; expected .int\\\(int, void \\\*\\\)." } */ -size_t fputs (const char*, struct StdioFile*); /* { dg-warning "conflicting types for built-in function .fputs.; expected .int\\\(const char \\\*, \[a-z_\]+ \\\*\\\)." } */ +unsigned long long fputs (const char*, struct StdioFile*); /* { dg-warning "conflicting types for built-in function .fputs.; expected .int\\\(const char \\\*, \[a-z_\]+ \\\*\\\)." } */ int fscanf (struct StdioFile*, const char*, size_t, ...); /* { dg-warning "conflicting types for built-in function .fscanf.; expected .int\\\(\[a-z_\]+ \\\*, const char \\\*, \.\.\.\\\)." } */ -int vfscanf (struct StdioFile*, const char*, ...); /* { dg-warning "conflicting types for built-in function .vfscanf.; expected .int\\\(\[a-z_\]+ \\\*, const char \\\*, \[a-z_\]+ \\\*\\\)." } */ +int vfscanf (struct StdioFile*, const char*, ...); /* { dg-warning "conflicting types for built-in function .vfscanf.; expected .int\\\(\[a-z_\]+ \\\*, const char \\\*, \[^\n\r,\\\)\]+\\\)." } */ size_t fwrite (const void*, size_t, size_t, struct StdioFile); /* { dg-warning "conflicting types for built-in function .fwrite.; expected .\(long \)?unsigned int\\\(const void \\\*, \(long \)?unsigned int, *\(long \)?unsigned int, *\[a-z_\]+ \\\*\\\)." } */ --- gcc/testsuite/gcc.dg/Wbuiltin-declaration-mismatch-11.c.jj 2019-01-26 12:31:26.035636194 +0100 +++ gcc/testsuite/gcc.dg/Wbuiltin-declaration-mismatch-11.c 2019-01-26 14:16:38.869483474 +0100 @@ -0,0 +1,21 @@ +/* PR c/86125 */ +/* { dg-do compile } */ +/* { dg-options "-Wbuiltin-declaration-mismatch -Wextra -Wno-ignored-qualifiers" } */ + +typedef __SIZE_TYPE__ size_t; +struct FILE; +struct tm; +struct fenv_t; +struct fexcept_t; +typedef struct FILE FILE; +typedef struct fenv_t fenv_t; +typedef struct fexcept_t fexcept_t; +typedef const int cint; +size_t strftime (char *__restrict, const size_t, const char *__restrict, /* { dg-bogus "mismatch in argument 1 type of built-in function" } */ + const struct tm *__restrict) __attribute__((nothrow)); +int fprintf (struct FILE *, const char *const, ...); /* { dg-bogus "mismatch in argument 2 type of built-in function" } */ +cint putc (int, struct FILE *); /* { dg-bogus "mismatch in return type of built-in function" } */ +cint fegetenv (fenv_t *); /* { dg-bogus "mismatch in argument 1 type of built-in function" } */ +cint fesetenv (const fenv_t *); /* { dg-bogus "mismatch in return type of built-in function" } */ +int fegetexceptflag (fexcept_t *, const int); /* { dg-bogus "mismatch in argument 1 type of built-in function" } */ +int fesetexceptflag (const fexcept_t *, const int); /* { dg-bogus "mismatch in argument 1 type of built-in function" } */ Jakub