Skip to content

Commit

Permalink
compilers: c: add Modern C -Werrors for GCC, Clang
Browse files Browse the repository at this point in the history
Add the following flags to error out by default via a new default-on 'Dc_legal_code'
option for GCC and Clang:
* -Werror=implicit (-> -Werror=implicit-int,implicit-function-declaration)
* -Werror=int-conversion
* -Werror=incompatible-pointer-types

Implicit function declarations were removed in C99 with no deprecation
period because of how dangerous they are.

Implicit conversions between integers and pointers were also never
allowed in >= C89.

These were allowed by GCC and Clang until recently for compatibility
reasons:
* GCC makes these an error by default in the upcoming GCC 14 release
* Clang made -Werror=int-conversion a default error in Clang 15 and made
the others an error in Clang 16

The reason for Meson to do this even for older compilers is straightforward:
* It'll take time for these newer versions to propagate into distributions.
* The code emitting these is broken *now*.
* Projects like PipeWire and various GNOME components are adding these flags
manually to catch them already.

Signed-off-by: Sam James <sam@gentoo.org>
  • Loading branch information
thesamesam committed Sep 10, 2024
1 parent f83dca3 commit bbfa547
Show file tree
Hide file tree
Showing 21 changed files with 64 additions and 19 deletions.
1 change: 1 addition & 0 deletions docs/markdown/Builtin-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ or compiler being used:
| c_args | | free-form comma-separated list | C compile arguments to use |
| c_link_args | | free-form comma-separated list | C link arguments to use |
| c_std | none | none, c89, c99, c11, c17, c18, c2x, c23, gnu89, gnu99, gnu11, gnu17, gnu18, gnu2x, gnu23 | C language standard to use |
| c_legal_code | true | true, false | Ban C dangerous constructs by default in C99 or later |
| c_winlibs | see below | free-form comma-separated list | Standard Windows libs to link against |
| c_thread_count | 4 | integer value ≥ 0 | Number of threads to use with emcc when using threads |
| cpp_args | | free-form comma-separated list | C++ compile arguments to use |
Expand Down
16 changes: 16 additions & 0 deletions docs/markdown/snippets/c-legal-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## `c_legal_code` option introduced

The new `c_legal_code` option is enabled by default. It bans
dangerous C constructs in C99 or later to emulate newer C compiler
defaults.

For GCC, it sets the following:
* `-Werror=implicit` (-> `-Werror=implicit-int,implicit-function-declaration`)
* `-Werror=int-conversion`
* `-Werror=incompatible-pointer-types`

For Clang, it sets the following:
* `-Werror=implicit` (-> `-Werror=implicit-int,implicit-function-declaration`)
* `-Werror=int-conversion`
* `-Werror=incompatible-pointer-types`
* `-Wno-error=incompatible-pointer-types-discards-qualifiers` (to emulate GCC's behavior)
30 changes: 29 additions & 1 deletion mesonbuild/compilers/c.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,15 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_

def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()

self.update_options(
opts,
self.create_option(options.UserBooleanOption,
self.form_compileropt_key('legal_code'),
'Ban use of dangerous constructs',
True),
)

if self.info.is_windows() or self.info.is_cygwin():
self.update_options(
opts,
Expand All @@ -169,6 +178,11 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]
std = options.get_value(key)
if std != 'none':
args.append('-std=' + std)

if options.get_value(self.form_compileropt_key('legal_code')):
if version_compare(self.version, '>=3.3.0') and std.value not in ('c89', 'c90', 'gnu89', 'gnu90'):
args.extend(('-Werror=implicit', '-Werror=int-conversion', '-Werror=incompatible-pointer-types', '-Wno-error=incompatible-pointer-types-discards-qualifiers'))

return args

def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
Expand Down Expand Up @@ -306,11 +320,20 @@ def get_options(self) -> 'MutableKeyedOptionDictType':
std_opt = opts[key]
assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(stds, gnu=True)

self.update_options(
opts,
self.create_option(options.UserBooleanOption,
self.form_compileropt_key('legal_code'),
'Ban use of dangerous constructs',
True),
)

if self.info.is_windows() or self.info.is_cygwin():
self.update_options(
opts,
self.create_option(options.UserArrayOption,
key.evolve('c_winlibs'),
self.form_compileropt_key('c_winlibs'),
'Standard Win libraries to link against',
gnu_winlibs),
)
Expand All @@ -322,6 +345,11 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]
std = options.get_value(key)
if std != 'none':
args.append('-std=' + std)

if options.get_value(self.form_compileropt_key('legal_code')):
if version_compare(self.version, '>=5.1.0') and version_compare(self.version, '<14.0.0') and std.value not in ('c89', 'c90', 'gnu89', 'gnu90'):
args.extend(('-Werror=implicit', '-Werror=int-conversion', '-Werror=incompatible-pointer-types'))

return args

def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/1 basic/meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Language are case unsensitive, check here that capital C works too.
project('valatest', 'vala', 'C')
project('valatest', 'vala', 'C', default_options: ['c_legal_code=false'])

valadeps = [dependency('glib-2.0'), dependency('gobject-2.0')]

Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/11 generated vapi/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('vapi-test', ['c', 'vala'])
project('vapi-test', ['c', 'vala'], default_options: ['c_legal_code=false'])

gnome = import('gnome')
subdir('libfoo')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('test glib target version and gresources', 'c', 'vala')
project('test glib target version and gresources', 'c', 'vala', default_options: ['c_legal_code=false'])

gnome = import('gnome')

Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/16 mixed dependence/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('mixed dependence', 'vala', 'c')
project('mixed dependence', 'vala', 'c', default_options: ['c_legal_code=false'])

cc = meson.get_compiler('c')

Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/17 plain consumer/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('plain consumer', 'vala', 'c')
project('plain consumer', 'vala', 'c', default_options: ['c_legal_code=false'])

deps = [dependency('glib-2.0'), dependency('gobject-2.0')]

Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/18 vapi consumed twice/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('vapi consumed twice', 'vala', 'c')
project('vapi consumed twice', 'vala', 'c', default_options: ['c_legal_code=false'])

base_deps = [dependency('glib-2.0'), dependency('gobject-2.0')]

Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/2 multiple files/meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# adding 'c' shouldn't be required
project('multiple files')
project('multiple files', default_options: ['c_legal_code=false'])
add_languages('vala')

glib = dependency('glib-2.0')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project( 'Genie multiple and mixed sources', 'vala', 'c' )
project( 'Genie multiple and mixed sources', 'vala', 'c', default_options: ['c_legal_code=false'])

genie_deps = [
dependency( 'glib-2.0' ),
Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/21 type module/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('valatest', 'c', 'vala')
project('valatest', 'c', 'vala', default_options: ['c_legal_code=false'])

glib_dep = dependency('glib-2.0')
gobject_dep = dependency('gobject-2.0')
Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/22 same target in directories/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('valatest', 'vala', 'c')
project('valatest', 'vala', 'c', default_options: ['c_legal_code=false'])

valadeps = [dependency('glib-2.0'), dependency('gobject-2.0')]
valafiles = files(
Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/25 extract_all_objects/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('valatest', 'vala', 'c')
project('valatest', 'vala', 'c', default_options: ['c_legal_code=false'])

valadeps = [dependency('glib-2.0'), dependency('gobject-2.0')]

Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/26 vala and asm/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('vala and asm', 'vala', 'c')
project('vala and asm', 'vala', 'c', default_options: ['c_legal_code=false'])

cpu = host_machine.cpu_family()
cc = meson.get_compiler('c')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('composite', 'vala', 'c')
project('composite', 'vala', 'c', default_options: ['c_legal_code=false'])
gnome = import('gnome')
deps = [
dependency('glib-2.0', version : '>=2.38'),
Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/3 dep/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('giotest', 'vala', 'c')
project('giotest', 'vala', 'c', default_options: ['c_legal_code=false'])

glib = dependency('glib-2.0')
gobject = dependency('gobject-2.0')
Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/4 config/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('valatest', 'vala', 'c')
project('valatest', 'vala', 'c', default_options: ['c_legal_code=false'])

valac = meson.get_compiler('vala')
# Try to find our library
Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/5 target glib/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('valatest', 'vala', 'c')
project('valatest', 'vala', 'c', default_options: ['c_legal_code=false'])

valadeps = [dependency('glib-2.0', version : '>=2.32'), dependency('gobject-2.0')]

Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/6 static library/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('valastatic', 'vala', 'c')
project('valastatic', 'vala', 'c', default_options: ['c_legal_code=false'])

valadeps = [dependency('glib-2.0'), dependency('gobject-2.0')]

Expand Down
2 changes: 1 addition & 1 deletion test cases/vala/7 shared library/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('shared library', 'vala', 'c')
project('shared library', 'vala', 'c', default_options: ['c_legal_code=false'])

valadeps = [dependency('glib-2.0'), dependency('gobject-2.0')]

Expand Down

0 comments on commit bbfa547

Please sign in to comment.