Skip to content

Commit

Permalink
make error()'s constant return value more visible
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
peff authored and gitster committed Dec 15, 2012
1 parent bfae342 commit e208f9c
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 0 deletions.
11 changes: 11 additions & 0 deletions git-compat-util.h
Expand Up @@ -288,6 +288,17 @@ extern NORETURN void die_errno(const char *err, ...) __attribute__((format (prin
extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));

/*
* Let callers be aware of the constant return value; this can help
* gcc with -Wuninitialized analysis. We have to restrict this trick to
* gcc, though, because of the variadic macro and the magic ## comma pasting
* behavior. But since we're only trying to help gcc, anyway, it's OK; other
* compilers will fall back to using the function as usual.
*/
#ifdef __GNUC__
#define error(fmt, ...) (error((fmt), ##__VA_ARGS__), -1)
#endif

extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
extern void set_error_routine(void (*routine)(const char *err, va_list params));

Expand Down
1 change: 1 addition & 0 deletions usage.c
Expand Up @@ -130,6 +130,7 @@ void NORETURN die_errno(const char *fmt, ...)
va_end(params);
}

#undef error
int error(const char *err, ...)
{
va_list params;
Expand Down

0 comments on commit e208f9c

Please sign in to comment.