make error()'s constant return value more visible

When git is compiled with "gcc -Wuninitialized -O3", some inlined calls provide an additional opportunity for the compiler to do static analysis on variable initialization. For example, with two functions like this: int get_foo(int *foo) { if (something_that_might_fail() < 0) return error("unable to get foo"); *foo = 0; return 0; } void some_fun(void) { int foo; if (get_foo(&foo) < 0) return -1; printf("foo is %d\n", foo); } If get_foo() is not inlined, then when compiling some_fun, gcc sees only that a pointer to the local variable is passed, and must assume that it is an out parameter that is initialized after get_foo returns. However, when get_foo() is inlined, the compiler may look at all of the code together and see that some code paths in get_foo() do not initialize the variable. As a result, it prints a warning. But what the compiler can't see is that error() always returns -1, and therefore we know that either we return early from some_fun, or foo ends up initialized, and the code is safe. The warning is a false positive. If we can make the compiler aware that error() will always return -1, it can do a better job of analysis. The simplest method would be to inline the error() function. However, this doesn't work, because gcc will not inline a variadc function. We can work around this by defining a macro. This relies on two gcc extensions: 1. Variadic macros (these are present in C99, but we do not rely on that). 2. Gcc treats the "##" paste operator specially between a comma and __VA_ARGS__, which lets our variadic macro work even if no format parameters are passed to error(). Since we are using these extra features, we hide the macro behind an #ifdef. This is OK, though, because our goal was just to help gcc. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Dec 15, 2012 at 12:37 UTC e208f9cc7574f5980faba498d0aa30b4defeb34f
2 files changed +12
git-compat-util.h
+11
@@ -288,6 +288,17 @@ extern NORETURN void die_errno(const char *err, ...) __attribute__((format (prin
288 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
289 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
290
291 +/*
292 + * Let callers be aware of the constant return value; this can help
293 + * gcc with -Wuninitialized analysis. We have to restrict this trick to
294 + * gcc, though, because of the variadic macro and the magic ## comma pasting
295 + * behavior. But since we're only trying to help gcc, anyway, it's OK; other
296 + * compilers will fall back to using the function as usual.
297 + */
298 +#ifdef __GNUC__
299 +#define error(fmt, ...) (error((fmt), ##__VA_ARGS__), -1)
300 +#endif
301 +
302 extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
303 extern void set_error_routine(void (*routine)(const char *err, va_list params));
304
usage.c
+1
@@ -130,6 +130,7 @@ void NORETURN die_errno(const char *fmt, ...)
130 va_end(params);
131 }
132
133 +#undef error
134 int error(const char *err, ...)
135 {
136 va_list params;