Skip to content

Commit e208f9c

Browse files
peffgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent bfae342 commit e208f9c

File tree

2 files changed

+12
-0
lines changed

2 files changed

+12
-0
lines changed

git-compat-util.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,17 @@ extern NORETURN void die_errno(const char *err, ...) __attribute__((format (prin
288288
extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
289289
extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
290290

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+
291302
extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
292303
extern void set_error_routine(void (*routine)(const char *err, va_list params));
293304

usage.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ void NORETURN die_errno(const char *fmt, ...)
130130
va_end(params);
131131
}
132132

133+
#undef error
133134
int error(const char *err, ...)
134135
{
135136
va_list params;

0 commit comments

Comments
 (0)