From patchwork Fri Feb 2 16:59:55 2024
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-Patchwork-Submitter: Florian Weimer
+ Function prototyping was added, first to help enforce type checking + on both the types of the arguments passed to the function, and also + to check the assignment compatibility of the function return type. + ++ +The initial ISO C standard and its 1999 revision removed support for +many C language features that were widely known as sources of +application bugs due to accidental misuse. For backwards +compatibility, GCC 13 and earlier compiler versions diagnosed use of +these features as warnings only. Although these warnings have been +enabled by default for many releases, experience shows that these +warnings are easily ignored, resulting in difficult-to-diagnose bugs. +In GCC 14, these issues are now reported as errors, and no output file +is created, providing clearer feedback to programmers that something +is wrong. + +
+Most components of the GNU project have already been fixed for +compatibility, but not all of them have had releases since fixes were +applied. Programs that bundle parts +of gnulib or +autoconf-archive +may need to update these sources files from their upstream versions. + +
+Several GNU/Linux distributions have shared patches from their porting +efforts on relevant upstream mailing lists and bug trackers, but of +course there is a strong overlap between programs that have these +historic C compatibility issues and are dormant today. + +
int
types (-Werror=implicit-int
)int
keyword
+addresses the error. For example, a flag like
+
++ static initialized; ++ +might turn into: + +
+ static int initialized; ++ +
If the return type of a function is omitted, the correct type to
+add could be int
or void
, depending on
+whether the function returns a value or not.
+
+
In some cases, the previously implied int
type
+was not correct. This mostly happens in function definitions
+that are not prototypes, when argument types are not
+declared outside the parameter list. Using the correct
+type maybe required to avoid int-conversion errors (see below).
+In the example below, the type of s
should be
+const char *
, not int
:
+
+
+ write_string (fd, s) + { + write (1, s, strlen (s)); + } ++ +The corrected standard C source code might look like this (still +disregarding error handling and short writes): + +
+ void + write_string (int fd, const char *s) + { + write (1, s, strlen (s)); + } ++ +
-Werror=implicit-function-declaration
)
+For well-known functions declared in standard headers, GCC provides
+fix-it hints with the appropriate #include
directives:
+
+
+error: implicit declaration of function ‘strlen’ [-Wimplicit-function-declaration] + 5 | return strlen (s); + | ^~~~~~ +note: include ‘<string.h>’ or provide a declaration of ‘strlen’ + +++ |+#include <string.h> + 1 | ++ +
+On GNU systems, headers described in standards (such as the C +standard, or POSIX) may require the definition of certain +macros at the start of the compilation before all required +function declarations are made available. +See Feature Test Macros +in the GNU C Library manual for details. + +
+Some programs are built with -std=c11
or
+similar -std
options that do not contain the
+string gnu
, but these programs still use POSIX or other
+extensions in standard C headers such as <stdio.h>
.
+The GNU C library automatically suppresses these extensions in
+standard C mode, which can result in implicit function declarations.
+To address this, the -std=c11
option can be
+dropped, -std=gnu11
can be used instead,
+or -std=c11 -D_DEFAULT_SOURCE
can be used re-enable
+common extensions.
+
+
+If undeclared functions from the same project are called and there is
+yet no suitable shared header file, you should add a declaration to a
+header file that is included by both the callers and the source file
+containing the function definition. This ensures that GCC checks that
+the prototype matches the function definition. GCC can perform type
+checks across translation units when building with options such as
+-flto -Werror=lto-type-mismatch
, which can help with
+adding appropriate prototypes.
+
+
+In some rare cases, adding a prototype may change ABI in inappropriate +ways. In these situations, it is necessary to declaration a function +without prototype: + +
+ void no_prototype (); ++ +However, this is an obsolete C feature that will change meaning in C23 +(declaration a function with a prototype and accepting no arguments, +similar to C++). Usually, a prototype with the default argument +promotions applied can be used instead. + +
+When building library code on GNU systems, it was possible to call +undefined (not just undeclared) functions and still run other code in +the library, particularly if ELF lazy binding was used. Only +executing the undefined function call would result in a lazy binding +error and program crash. + +
-Werror=declaration-missing-parameter-type
)-Wdeclaration-missing-parameter-type
warning. The
+second line in the following example is now treated as an error
+(previously this resulted in an unnamed warning):
+
++ typedef int *int_array; + int first_element (intarray); ++ +The fix is to correct the spelling mistake: + +
+ typedef int *int_array; + int first_element (int_array); ++ +GCC will type-check function arguments after that, potentially +requiring further changes. (Previously, the function declaration was +treated as not having no prototype.) + +
return
statement (-Werror=return-mismatch
)return
statements with expressions
+in functions which are declared to return void
, or
+return
statements without expressions for functions
+returning a nonvoid
type.
+
+
+To address this, remove the incorrect expression (or turn it into a
+statement expression immediately prior to the return
+statements if the expression has side effects), or add a dummy return
+value, as appropriate. If there is no suitable dummy return value,
+further changes may be needed to implement appropriate error handling.
+
+
+Previously, these mismatches were diagnosed as
+a -Wreturn-type
warning. This warning still exists, and
+is not treated as an error by default. It now covers remaining
+potential correctness issues, such as reaching the closing
+brace }
of function that does not
+return void
.
+
+
+By default, GCC still accepts returning an expression of
+type void
from within a function that itself
+returns void
, as a GNU extension that matches C++ rules
+in this area.
+
+
-Werror=int-conversion
)
+It makes sense to address missing int
type, implicit
+function declarations, and incorrect return
statement
+usage prior to tackling these int
-conversion issues.
+Some of them will be caused by missing types resulting
+in int
, and the default int
return type of
+implicitly declared functions.
+
+
To fix the remaining int
-conversions issues, add casts
+to an appropriate pointer or integer type. On GNU systems, the
+standard (but generally optional) types
+intptr_t
and uintptr_t
(defined
+in <stdint.h>
) are always large enough to store all
+pointer values. If you need a generic pointer type, consider
+using void *
.
+
+
Note that in some cases, it may be more appropriate to pass the +address of an integer variable instead of a cast of the variable's +value. + +
-Werror=incompatible-pointer-types
)void *
type and
+its qualified variations.
+
+
+To fix the compilation errors resulting from that, you can add the
+appropriate casts, and maybe consider using code void *
+in more places (particularly for old programs that predate the
+introduction of void
into the C language).
+
+
+Programs that do not carefully track pointer types are likely to
+contain aliasing violations, so consider building
+with -fno-strict-aliasing
as well. (Whether the casts
+are written manually or performed by GCC automatically does not make a
+difference in terms of strict aliasing violations.)
+
+
+A frequent source of incompatible function pointer types involves +callback functions that have more specific argument types (or less +specific return types) than the function pointer they are assigned to. +For example, old code which attempts to sort an array of strings might +look like this: + +
+#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +int +compare (char **a, char **b) +{ + return strcmp (*a, *b); +} + +void +sort (char **array, size_t length) +{ + qsort (array, length, sizeof (*array), compare); +} ++ +To correct this, the callback function should be defined with the +correct type, and the arguments should be cast as appropriate before +they are used (as calling a function through a function pointer of an +incorrect type is undefined): + +
+int +compare (const void *a1, const void *b1) +{ + char *const *a = a1; + char *const *b = b1; + return strcmp (*a, *b); +} ++ +
+A similar issue can arise with object pointer types. Consider a
+function that is declared with an argument of type
+void **
, and you want to pass the address of a variable
+of type int *
:
+
+
+extern int *pointer; +extern void operate (int command, void **); + +operate (0, &pointer); ++ +In these cases, it may be appropriate to make a copy of the pointer +with the correct
void *
type:
+
++extern int *pointer; +extern void operate (int command, void **); + +void *pointer1 = pointer; +operate (0, &pointer1); +pointer = pointer1; ++ +As mentioned initially, adding the cast here would not eliminate any +strict aliasing violation in the implementation of +the
operate
function. Of course in general, introducing
+such additional copies may alter program behavior.
+
+
+Some programming styles rely on implicit casts between related object
+pointer types to implement C++-style struct
inheritance.
+It may be possible to avoid writing manual casts with
+the -fplan9-extensions
options and unnamed
+initial struct
fields for the base type in
+derived struct
s.
+
+
+Some programs use a concrete function pointer type such as
+void (*) (void)
as a generic function pointer type
+(similar to void *
for object pointer types), and rely on
+implicit casts to and from this type. The reason for that is that C
+does not offer a generic function pointer type, and standard C
+disallows casts between function pointer types and object pointer
+types. On most targets, GCC supports implicit conversion
+between void *
and function pointer types. However, for
+a portable solution, the concrete function pointer type needs to be
+used, together with explicit casts.
+
+
+In cases where this is a concern, generated config.log
,
+config.h
and other source code files can be compared
+using diff,
+to ensure there are no unexpected differences.
+
+
+This phenomenon also impacts similar procedures part of CMake, Meson, +and various build tools for C extension modules of scripting +languages. + +
+Autoconf has supported C99 compilers since at least version 2.69 in +its generic, core probes. (Specific functionality probes may have +issues addressed in more recent versions.) Versions before 2.69 may +have generic probes (for example for standard C support) that rely on +C features that were removed in 1999 and thus fail with GCC 14. + +
+Sources that cannot be ported to standard C can be compiled
+with -fpermissive
, -std=gnu89
,
+or -std=c89
. Despite their names, the latter two options
+turn on support for pre-standard C constructs, too. With the
+-fpermissive
options, programs can use C99 inlining
+semantics and features that were removed from C99. Alternatively,
+individual warnings can be downgraded to warnings using
+the -Wno-error=…
option, or disabled complete
+with -Wno-…
. For example,
+-Wno-error=incompatible-pointer-types
turns off most type
+checking for pointer assignments.
+
+
+Some build systems do not pass the CFLAGS
environment
+or make
variable to all parts of the builds, and may
+require setting CC
to something like gcc
+-fpermissive
instead. If the build system does not support
+whitespace in the CC
variable, a wrapper script like this
+may be required:
+
+
+#!/bin/sh +exec /usr/bin/gcc -fpermissive "$@" ++ +
#pragma GCC diagnostic warning
directives to
+turn these errors back into warnings:
+
++#if defined __GNUC__ && __GNUC__ >= 14 +#pragma GCC diagnostic warning "-Wimplicit-function-declaration" +#pragma GCC diagnostic warning "-Wincompatible-pointer-types" +#pragma GCC diagnostic warning "-Wint-conversion" +#pragma GCC diagnostic warning "-Wreturn-mismatch" +#endif ++ +Not listed here are
-Wimplicit-int
+and -Wdeclaration-missing-parameter-type
because they
+should be straightforward to address in a code generator.
+
+
+It is unclear at which point GCC can enable the C23 bool
+keyword by default (making the bool
type available
+without including #include <stdbool.h>
explicitly).
+Many programs define their own bool
types, sometimes with
+a different size of the already-available _Bool
type. A
+further complication is that even if the sizes are the same, a custom
+bool
typically does not have trap representations,
+while _Bool
and the new bool
type do. This
+means that there can be subtle compatibility issues, particularly when
+processing untrusted, not necessarily well-formed input data.
+
+
+GCC is unlikely to warn about function declarations that are not +prototypes by default. This means that there is no stringent reason +to turn + +
+void do_something (); ++ +into + +
+void do_something (void); ++ +except for diagnosing extraneous ignored arguments as errors. A +future version of GCC will likely warn about calls to functions +without a prototype which specify such extraneous arguments +(
do_something (1)
, for example). Eventually, GCC will
+diagnose such calls as errors because they are constraint violations
+in C23.
+
++GCC will probably continue to support old-style function definitions +even once C23 is used as the default language dialect. +