Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compilers: c: add Modern C -Werrors for GCC, Clang (again) #13659

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

thesamesam
Copy link
Collaborator

@thesamesam thesamesam commented Sep 10, 2024

Retry of #12682.

Commits:

  • The first commit is just compilers: c: add Modern C -Werrors for GCC, Clang #12682 rebased.
  • The second is switching to a builtin option. This avoids us having to add one for each language (see next point). It also gives us more scope in future for "this is so important it should really be a default"-style flags but compilers don't (yet) set it for one reason or another.
  • The third is wiring this up for LTO too where the LTO-specific options are relevant to at least C++, but I think it may apply to Fortran as well as any other language GCC can do (it supports LTO across all FEs).

If we get consensus on how to move forward, I'll squash & fix up the commit messages for the other commits & docs, ofc.

--

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.

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>
@@ -295,6 +296,10 @@ def get_base_compile_args(options: 'KeyedOptionDictType', compiler: 'Compiler',
mode=get_option_value(options, OptionKey('b_lto_mode'), 'default')))
except (KeyError, AttributeError):
pass

if option_enabled(compiler.base_options, options, OptionKey('b_legal_code')):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eli-schwartz This conditional is always false but I don't get why?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still need to add to the compiler that it suppots b_legal_code, say in the GnuCompiler.__init__ method

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although, come to think of it, GnuDCompiler inherits that, as does the out of tree GnuRustCompiler patches I have somewhere...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I was wondering if it was something like that but I couldn't spot it. Doh.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, probably have to add them to `GnuCCompiler and GnuCPPCompiler

Signed-off-by: Sam James <sam@gentoo.org>
For GCC LTO, we can set the following options with b_legal_code:
* -Werror=lto-type-mismatch
* -Werror=odr
* -Werror=strict-aliasing

TODO: write more

Signed-off-by: Sam James <sam@gentoo.org>
@dnicolodi
Copy link
Member

This may have been discussed elsewhere already, but why does Meson need to have a built-in option to enable an (opinionated?) set of compilation flags? Is this only a way to avoid some projects to have to repeat a few lines into their meson.build?

If this is really needed, the option name does a very poor job in describing what it does. b_legal_code=true has the effect of making some code that would be allowed to compile into code that does not compile anymore, thus it turns legal code into illegal code: not what I would have expected from the option name.

@@ -234,6 +234,7 @@ def init_option(self, name: OptionKey) -> options._U:
OptionKey('b_thinlto_cache_dir'): BaseOption(options.UserStringOption, 'Directory to store ThinLTO cache objects', ''),
OptionKey('b_sanitize'): BaseOption(options.UserComboOption, 'Code sanitizer to use', 'none',
choices=['none', 'address', 'thread', 'undefined', 'memory', 'leak', 'address,undefined']),
OptionKey('b_legal_code'): BaseOption(options.UserBooleanOption, 'Whether to ban dangerous constructs in a language', True),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't having this enable by default going to break compilation for a lot of existing projects?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These people are going to find their code fails to compile as soon as they upgrade to 2024 compilers. It's a "don't shoot the messenger" scenario.

The constructs aren't "dangerous", they are "the C standard as released in c99, says that this is not real C code and a C compiler shouldn't successfully compile it". It's also Undefined Behavior, and a particular kind of UB that results in programs having a high tendency to crash on non-amd64 architectures. That's why GCC and clang have started actually enforcing the text of the standard, and making the compiler error out on it.

It's a user service to make that visible to people by default. There's no point in having the option if no one uses it. But an option is necessary so that people can disable it, if they have a codebase originally written in 1991 and cannot update it to conform to the 1999 standard.


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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "newer C compiler" mean here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means gcc 14 or clang 16.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants