Here is the alternative approach.
Bootstrapped and regression tested for x86_64.
c: remaining fix for the composite type inconsistency [PR120510]
There is an old GNU extension which allows overriding the
promoted old-style arguments when there is an earlier prototype
An example (from a test added for PR16666) is the following.
float dremf (float, float);
float
dremf (x, y)
float x, y;
{
return x + y;
}
The types of the two declarations are not compatible, because
the arguments are not self-promoting. Add a special case
to function_types_compatible_p that can be toggled via a flag
for comptypes_internal and add a helper function to be able to
add the checking assertions to composite_type.
PR c/120510
gcc/c/ChangeLog:
* c-typeck.cc (composite_type_internal): Activate checking
assertions for all types and also inputs.
(comptypes_for_composite_check): New helper function.
(function_types_compatible_p): Add exception.
gcc/testsuite/ChangeLog:
* gcc.dg/old-style-prom-4.c: New test.
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index ae3e7068729..e24629be918 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -136,6 +136,7 @@ static int lvalue_or_else (location_t, const_tree, enum
lvalue_use);
static void record_maybe_used_decl (tree);
static bool comptypes_internal (const_tree, const_tree,
struct comptypes_data *data);
+static bool comptypes_check_for_composite (tree t1, tree t2);
/* Return true if EXP is a null pointer constant, false otherwise. */
@@ -1002,15 +1003,13 @@ composite_type_internal (tree t1, tree t2, struct
composite_cache* cache)
tree
composite_type (tree t1, tree t2)
{
+ gcc_checking_assert (comptypes_check_for_composite (t1, t2));
+
struct composite_cache cache = { };
tree n = composite_type_internal (t1, t2, &cache);
- /* For function types there are some cases where qualifiers do
- not match. See PR120510. */
- if (FUNCTION_TYPE != TREE_CODE (n))
- {
- gcc_checking_assert (comptypes (n, t1));
- gcc_checking_assert (comptypes (n, t2));
- }
+
+ gcc_checking_assert (comptypes_check_for_composite (n, t1));
+ gcc_checking_assert (comptypes_check_for_composite (n, t2));
return n;
}
@@ -1454,16 +1453,39 @@ comptypes_verify (tree type1, tree type2)
}
struct comptypes_data {
+
+ /* output */
bool enum_and_int_p;
bool different_types_p;
bool warning_needed;
+
+ /* context */
bool anon_field;
bool pointedto;
+
+ /* configuration */
bool equiv;
+ bool ignore_promoting_args;
const struct tagged_tu_seen_cache* cache;
};
+
+/* Helper function for composite_type. This function ignores when the
+ function type of an old-style declaration is incompatible with a type
+ of a declaration with prototype because some are arguments are not
+ self-promoting. This is ignored only for function types but not
+ ignored in a nested context. */
+
+static bool
+comptypes_check_for_composite (tree t1, tree t2)
+{
+ struct comptypes_data data = { };
+ data.ignore_promoting_args = FUNCTION_TYPE == TREE_CODE (t1);
+ return comptypes_internal (t1, t2, &data);
+}
+
+
/* C implementation of compatible_types_for_indirection_note_p. */
bool
@@ -1593,6 +1615,10 @@ comptypes_equiv_p (tree type1, tree type2)
permitted in C11 typedef redeclarations, then this sets
'different_types_p' in DATA to true; it is never set to
false, but may or may not be set if the types are incompatible.
+ If two functions types are not compatible only because one is
+ an old-style definition that does not have self-promoting arguments,
+ then this can be ignored by setting 'ignore_promoting_args_p'.
+ For 'equiv' we can compute equivalency classes (see above).
This differs from comptypes, in that we don't free the seen
types. */
@@ -2030,9 +2056,14 @@ function_types_compatible_p (const_tree f1, const_tree
f2,
ret2 = build_qualified_type (TYPE_MAIN_VARIANT (ret2),
TYPE_QUALS (ret2) & ~TYPE_QUAL_VOLATILE);
+ bool ignore_pargs = data->ignore_promoting_args;
+ data->ignore_promoting_args = false;
+
if (!comptypes_internal (ret1, ret2, data))
return false;
+ data->ignore_promoting_args = ignore_pargs;
+
tree args1 = TYPE_ARG_TYPES (f1);
tree args2 = TYPE_ARG_TYPES (f2);
@@ -2046,8 +2077,9 @@ function_types_compatible_p (const_tree f1, const_tree f2,
{
if (TYPE_NO_NAMED_ARGS_STDARG_P (f1) != TYPE_NO_NAMED_ARGS_STDARG_P (f2))
return false;
- if (!self_promoting_args_p (args2))
+ if (!(data->ignore_promoting_args || self_promoting_args_p (args2)))
return false;
+ data->ignore_promoting_args = false;
/* If one of these types comes from a non-prototype fn definition,
compare that with the other type's arglist.
If they don't match, ask for a warning (but no error). */
@@ -2060,8 +2092,9 @@ function_types_compatible_p (const_tree f1, const_tree f2,
{
if (TYPE_NO_NAMED_ARGS_STDARG_P (f1) != TYPE_NO_NAMED_ARGS_STDARG_P (f2))
return false;
- if (!self_promoting_args_p (args1))
+ if (!(data->ignore_promoting_args || self_promoting_args_p (args1)))
return false;
+ data->ignore_promoting_args = false;
if (TYPE_ACTUAL_ARG_TYPES (f2)
&& !type_lists_compatible_p (args1, TYPE_ACTUAL_ARG_TYPES (f2), data))
data->warning_needed = true;
diff --git a/gcc/testsuite/gcc.dg/old-style-prom-4.c
b/gcc/testsuite/gcc.dg/old-style-prom-4.c
new file mode 100644
index 00000000000..3632fa6ab31
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/old-style-prom-4.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu17" } */
+
+int g(float a, float b);
+int g(a, b)
+ float a;
+ float b;
+{
+}
+
+int f(float a, float (*b)()); /* { dg-error "prototype declaration" } */
+
+int f(a, b)
+ float a;
+ float (*b)(float); /* { dg-error "match prototype" } */
+{
+}
+
+int (*e(float a))(float (*b)());
+
+int (*e(a))(float (*b)(float)) /* { dg-error "conflicting types" } */
+ float a;
+{
+}
+