[PATCH] xx

Ralph Boehme slow at samba.org
Wed Apr 27 18:23:28 UTC 2016


Hi!

I just came across an issue caused by our build system not forcing a
specific C standard, instead relying on cherry-picking C99 features.

One C99 feature we actively use in our code base and check for at
configure time are C99 designated initializers.

But what about other C99 features? What about C99 intermingled code
and variable declarations, eg declaring the loop variable in a for
loop:

  for (int i = 0; ...something...; i++) { ; }

This builds just fine in a Samba build with newer GCC that have a
default C standard of at least gnu99, but the build then breaks with
older gcc that have a default C standard of gnu90. And the poor
developer with the newer gcc won't notice.

gnu90 includes C99 designated initializers (yes, really), but not C99
intermingled code and variable declarations:

    $ cat test.c
    struct foo {int x; char y;};
    struct foo bar = { .y = 'X', .x = 1 };
    
    int baz(void) {
        for (int i=0; i < 2; i++) {
            bar.x += i;
        }
        return bar.x;
    }
    
    $ gcc -std=gnu90 -c test.c
    test.c: In function ‘baz’:
    test.c:5:2: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
      for (int i=0; i < 2; i++) {
      ^
    test.c:5:2: note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code

What can we do?

If we decide we *don't* want to support specific C99 features like
mixed declaration and code, there's just no way to say to the
compiler "I want C99 feaure A but I don't want C99 feature B, please
warn me if I inadvertently use it".

So I guess cherry-picking is the wrong approach. It should be all or
nothing.

Attached is a patch that would continue cherry-picking C99 features
and adds a check for mixed declarations and code, but this is just
meant to illustrate the problem.

Maybe we should take this further and be as pedantic as autoconf's
AC_PROG_CC_C99:

# If the C compiler is not in ISO C99 mode by default, try to add an
# option to output variable CC to make it so.  This macro tries
# various options that select ISO C99 on some system or another.  It
# considers the compiler to be in ISO C99 mode if it handles _Bool,
# // comments, flexible array members, inline, long long int, mixed
# code and declarations, named initialization of structs, restrict,
# va_copy, varargs macros, variable declarations in for loops and
# variable length arrays.

Cheerio!
-slow
-------------- next part --------------
From 73264ad52e68090056685969a8d7fbf55fa8a756 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 27 Apr 2016 16:05:29 +0200
Subject: [PATCH] waf: correct C99 detection for older GCC with a default of
 -std=gnu90
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Older GCC compilers have a default C standard of gnu90. That includes
C90, some GNU extensions and some parts of C99, including designated
initializers, but *not* intermingled code and variable declarations.

Our current waf check for C99 mode only test the needed compiler flags
for designated initializers, starting checks without extra flags. This
succeeds, as a result the compiler is in gnu90 mode which is not what we
want.

As an example, this code compiles with -std=gnu90:

    struct foo {int x; char y;};
    struct foo bar = { .y = 'X', .x = 1 };

because designated initializers are included in gnu90.

But code with mixed variable declaration (int i in for loop) will fail,
because that is *not* part of gnu90:

    struct foo {int x; char y;};
    struct foo bar = { .y = 'X', .x = 1 };

    int baz(void) {
        for (int i=0; i < 2; i++) {
	    bar.x += i;
        }
        return bar.x;
    }

gcc -std=gnu90 -c test.c
test.c: In function ‘baz’:
test.c:5:2: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
  for (int i=0; i < 2; i++) {
  ^
test.c:5:2: note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code

Newer GCC versions default to -std=gnu11 and so don't exhibit this
problem.

To fix this, enhance our check which compiler flags are needed for C99
mode by adding code to the waf check that uses intermingled code and
variable declarations.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 buildtools/wafsamba/wscript | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/buildtools/wafsamba/wscript b/buildtools/wafsamba/wscript
index 8802e5a..08704d1 100755
--- a/buildtools/wafsamba/wscript
+++ b/buildtools/wafsamba/wscript
@@ -465,11 +465,14 @@ def configure(conf):
         conf.DEFINE('_BSD_TYPES', 1, add_to_cflags=True)
 
     # Try to find the right extra flags for C99 initialisers
-    for f in ["", "-AC99", "-qlanglvl=extc99", "-qlanglvl=stdc99", "-c99"]:
-        if conf.CHECK_CFLAGS([f], '''
-struct foo {int x;char y;};
-struct foo bar = { .y = 'X', .x = 1 };
-'''):
+    # and C99 intermingled declarations and code
+    for f in ["", "-std=c99", "-AC99", "-qlanglvl=extc99", "-qlanglvl=stdc99", "-c99"]:
+        if conf.CHECK_CODE('''
+            struct foo {int x;char y;};
+            struct foo bar = { .y = 'X', .x = 1 };
+            for (int i=0; i<2; i++) { bar.x+=i; }
+            ''',
+            define='HAVE_C99', cflags=f, addmain=True):
             if f != "":
                 conf.ADD_CFLAGS(f)
             break
-- 
2.5.0



More information about the samba-technical mailing list