c: C2x changes to function type compatibility

WG14 N2432, the C2x removal of old-style function definitions, also
changed the function type compatibility rules so that an unprototyped
declaration can be compatible with a non-variadic prototyped
declaration even if some function arguments are changed by the default
argument promotions.  I missed that change in the initial
implementation for GCC of the rest of the N2432 changes, but
discussion on the WG14 reflector in February suggests that this is
indeed an intended change.  Implement this in the C front end.

Note that while this may be of use in some cases for use of pointers
to unprototyped function types as a kind of generic function pointer,
it's *not* possible to call such a function without a prototype
visible, without getting runtime undefined behavior from the
(promoted) type used in the call being incompatible with the
(unpromoted) type in the prototype.

Note also that GCC has a longstanding extension to allow compatibility
of such a prototype with an old-style definition specifying the same
type as in the prototype (which is not valid in ISO C, before
old-style definitions were removed in C2x).

Bootstrapped with no regressions for x86_64-pc-linux-gnu.

gcc/c/
	* c-typeck.c (function_types_compatible_p): For C2X, treat
	unprototyped function as compatible with non-variadic prototyped
	function even if some argument types are changed by the default
	argument promotions.

gcc/testsuite/
	* gcc.dg/c11-unproto-1.c, gcc.dg/c11-unproto-2.c,
	gcc.dg/c2x-unproto-1.c, gcc.dg/c2x-unproto-2.c: New tests.
This commit is contained in:
Joseph Myers 2021-04-29 00:50:35 +00:00
parent e4ff4ffb43
commit cc80612621
5 changed files with 88 additions and 2 deletions

View File

@ -1692,7 +1692,7 @@ function_types_compatible_p (const_tree f1, const_tree f2,
if (args1 == NULL_TREE)
{
if (!self_promoting_args_p (args2))
if (flag_isoc2x ? stdarg_p (f2) : !self_promoting_args_p (args2))
return 0;
/* If one of these types comes from a non-prototype fn definition,
compare that with the other type's arglist.
@ -1705,7 +1705,7 @@ function_types_compatible_p (const_tree f1, const_tree f2,
}
if (args2 == NULL_TREE)
{
if (!self_promoting_args_p (args1))
if (flag_isoc2x ? stdarg_p (f1) : !self_promoting_args_p (args1))
return 0;
if (TYPE_ACTUAL_ARG_TYPES (f2)
&& type_lists_compatible_p (args1, TYPE_ACTUAL_ARG_TYPES (f2),

View File

@ -0,0 +1,24 @@
/* Test compatibility of unprototyped and prototyped function types (C2x makes
the case of types affected by default argument promotions compatible). Test
valid-in-C2x usages are not accepted for C11. */
/* { dg-do compile } */
/* { dg-options "-std=c11 -pedantic-errors" } */
void f1 (); /* { dg-message "previous declaration" } */
void f1 (float); /* { dg-error "conflicting types" } */
/* { dg-message "default promotion" "" { target *-*-* } .-1 } */
void f2 (float); /* { dg-message "previous declaration" } */
void f2 (); /* { dg-error "conflicting types" } */
/* { dg-message "default promotion" "" { target *-*-* } .-1 } */
void f3 (); /* { dg-message "previous declaration" } */
void f3 (char); /* { dg-error "conflicting types" } */
/* { dg-message "default promotion" "" { target *-*-* } .-1 } */
void f4 (char); /* { dg-message "previous declaration" } */
void f4 (); /* { dg-error "conflicting types" } */
/* { dg-message "default promotion" "" { target *-*-* } .-1 } */
/* Built-in function case. */
float sqrtf (); /* { dg-warning "conflicting types for built-in function" } */

View File

@ -0,0 +1,21 @@
/* Test compatibility of unprototyped and prototyped function types (C2x makes
the case of types affected by default argument promotions compatible). Test
invalid-in-C2x usages, in C11 mode. */
/* { dg-do compile } */
/* { dg-options "-std=c11 -pedantic-errors" } */
void f1 (); /* { dg-message "previous declaration" } */
void f1 (int, ...); /* { dg-error "conflicting types" } */
/* { dg-message "ellipsis" "" { target *-*-* } .-1 } */
void f2 (int, ...); /* { dg-message "previous declaration" } */
void f2 (); /* { dg-error "conflicting types" } */
/* { dg-message "ellipsis" "" { target *-*-* } .-1 } */
void f3 (); /* { dg-message "previous declaration" } */
void f3 (char, ...); /* { dg-error "conflicting types" } */
/* { dg-message "ellipsis" "" { target *-*-* } .-1 } */
void f4 (char, ...); /* { dg-message "previous declaration" } */
void f4 (); /* { dg-error "conflicting types" } */
/* { dg-message "ellipsis" "" { target *-*-* } .-1 } */

View File

@ -0,0 +1,20 @@
/* Test compatibility of unprototyped and prototyped function types (C2x makes
the case of types affected by default argument promotions compatible). Test
valid-in-C2x usages. */
/* { dg-do compile } */
/* { dg-options "-std=c2x -pedantic-errors" } */
void f1 ();
void f1 (float);
void f2 (float);
void f2 ();
void f3 ();
void f3 (char);
void f4 (char);
void f4 ();
/* Built-in function case. */
float sqrtf ();

View File

@ -0,0 +1,21 @@
/* Test compatibility of unprototyped and prototyped function types (C2x makes
the case of types affected by default argument promotions compatible). Test
invalid-in-C2x usages. */
/* { dg-do compile } */
/* { dg-options "-std=c2x -pedantic-errors" } */
void f1 (); /* { dg-message "previous declaration" } */
void f1 (int, ...); /* { dg-error "conflicting types" } */
/* { dg-message "ellipsis" "" { target *-*-* } .-1 } */
void f2 (int, ...); /* { dg-message "previous declaration" } */
void f2 (); /* { dg-error "conflicting types" } */
/* { dg-message "ellipsis" "" { target *-*-* } .-1 } */
void f3 (); /* { dg-message "previous declaration" } */
void f3 (char, ...); /* { dg-error "conflicting types" } */
/* { dg-message "ellipsis" "" { target *-*-* } .-1 } */
void f4 (char, ...); /* { dg-message "previous declaration" } */
void f4 (); /* { dg-error "conflicting types" } */
/* { dg-message "ellipsis" "" { target *-*-* } .-1 } */