I have mostly convinced myself that I have encountered some g++ 4.8.3 bug, but I thought I would ask this list first because I have very little experience with setjmp/longjmp. I have simplified my code in question to the following foo.cxx:
#include <setjmp.h>
#include <string.h>
// Changing MyStruct to be just a single int makes the compiler happy.
struct MyStruct
{
int a;
int b;
};
// Setting MyType to int makes the compiler happy.
#ifdef USE_STRUCT
typedef MyStruct MyType;
#elif USE_INT
typedef int MyType;
#endif
void SomeFunc(MyType val)
{
}
static void static_func(MyType val)
{
SomeFunc(val);
}
int main(int argc, char **argv)
{
jmp_buf env;
if (setjmp(env))
{
return 1;
}
MyType val;
#ifdef USE_STRUCT
val.a = val.b = 0;
#elif USE_INT
val = 0;
#endif
// Enabling the below memset call makes the compiler happy.
//memset(&val, 0, sizeof(val));
// Iterating 1 or 2 times makes the compiler happy.
for (unsigned i = 0; i < 3; i++)
{
// calling SomeFunc() directly makes the compiler happy.
static_func(val);
}
return 0;
}
I use g++ 4.8.3 to compile this code. What's interesting to me is that when I define USE_STRUCT, the compilation fails but succeeds with USE_INT. There are comments in the code that further indicate how to make compilation succeed with USE_STRUCT. Compilation only fails also with the -fPIC option to g++, but this is a required argument in my environment.
To see the compilation error:
g++ -DUSE_STRUCT -Wextra -Wno-unused-parameter -O3 -Werror -fPIC foo.cxx
foo.cxx: In function ‘int main(int, char**)’:
foo.cxx:26:5: error: variable ‘val’ might be clobbered by ‘longjmp’ or ‘vfork’ [-Werror=clobbered]
But using a simple int is OK:
g++ -DUSE_INT -Wextra -Wno-unused-parameter -O3 -Werror -fPIC foo.cxx
Can someone please explain to me why val might be clobbered if it is a struct but not if it is an int? Any insights on the other ways to make compilation succeed with the struct, as indicated in the comments in the code? Or is this pointing to a compiler bug?
Any insights and comments are greatly appreciated.
setjmp()
saves the current stack. Since it's called before the declaration of val
, that variable won't be in the saved stack.
After setjmp()
, the variable is initialized and if the code later jumps back to the setjmp
point, the variable will be initialized again, clobbering the old variable. If there would be a non-trivial destructor that should be called on the old instance, this is undefined behavior (§18.10/4):
A
setjmp
/longjmp
call pair has undefined behavior if replacing thesetjmp
andlongjmp
bycatch
andthrow
would invoke any non-trivial destructors for any automatic objects.
Likely the destructor of the old instance won't be called. My guess would be that gcc doesn't warn for primitive types, since they don't have destructors, but warns for more complicated types where this might be problematic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With