TL;DR: I want
int some_opaque_error_handler() __attribute__((returns_nonzero));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ equivalent of this
A C/C -based library I use uses return-codes to indicate errors, which causes the compiler to complain about using "unitialized" values. For example
int foo(int **ptr)
{
// detail not important here, just that:
// 1. compiler sees the contents of foo() from bar()
// 2. foo() has some kind of early return in error-path
// 3. precise return value of foo() from early return not known to the compiler
// 4. ptr is 100% properly initialized in non-error path (i.e. warning is
// indeed spurious)
if (ptr == 0xdeadbeef) {
return some_opaque_error_handler(...); // returns nonzero
}
*ptr = properly_initialize();
return 0;
}
int bar()
{
int *p;
if (foo(&p)) {
return /* continue propogating the error yadda yadda yadda */;
}
*p = 1; // Warning: p may be used uninitialized
}
In this case *p
is never accessed from bar()
if foo()
returns an error, but since the error handler is opaque the compiler cannot know this.
Is there anyway to let the compiler know that returning via error handler is almost always[1] nonzero (and hence any subsequent accesses never occur?)[2]. I have similar issues running static analyzers, since they too assume that execution may continue past such points.
[1] Users may change the behavior of the error handler to -- as unlikely as it may be -- return 0
so any solution/compiler hint must be nonbinding.
[2] Yes I know I can simply initialize the variables (which is what I currently do) but:
- There are a metric boatload of such cases
- These warnings seem to be heavily dependent on compiler, compiler version, optimization level, and orientation of Saturns rings with respect to the galactic plane
So this feels like an unwinnable game of whack-a-mole
CodePudding user response:
If you are willing to use a macro, you can hack your function to look like this:
#define some_opaque_error_handler(...) \
(some_opaque_error_handler(__VA_ARGS__) ?: -1)
This relies on a GCC extension where leaving out the middle operand gives the tested value if it is non-zero.
CodePudding user response:
Mark the function as accessing for write.
__attribute__((__access__(__write_only__, 1)))
int foo(int **ptr) {
I want
int some_opaque_error_handler() __attribute__((returns_nonzero));
You would do this:
#define some_opaque_error_handler() \
__extension__({ \
int _x = some_opaque_error_handler(); \
if (_x == 0) __builtin_unreachable(); \
_x; \
})
or the same on calling site.