5/27/2010

05-27-10 - Weird Compiler Error

Blurg just fought one of the weirder problems I've ever seen.

Here's the simple test case I cooked up :


void fuck()
{
#ifdef RR_ASSERT
#pragma RR_PRAGMA_MESSAGE("yes")
#else
#pragma RR_PRAGMA_MESSAGE("no")
#endif

    RR_ASSERT(true);
}

And here is the compiler error :

1>.\rrSurfaceSTBI.cpp(43) : message: yes
1>.\rrSurfaceSTBI.cpp(48) : error C3861: 'RR_ASSERT': identifier not found

Eh !? Serious WTF !? I know RR_ASSERT is defined, and then it says it's not found !? WTF !?

Well a few lines above that is the key. There was this :


#undef  assert
#define assert  RR_ASSERT

which seems like it couldn't possibly cause this, right? It's just aliasing the standard C assert() to mine. Not possible related, right? But when I commented out that bit the problem went away. So of course my first thought is clean-rebuild all, did I have precompiled headers on by mistake? etc. I assume the compiler has gone mad.

Well, it turns out that somewhere way back in RR_ASSERT I was in a branch that caused me to have this definition for RR_ASSERT :


#define RR_ASSERT(exp)  assert(exp)

This creates a strange state for the preprocessor. RR_ASSERT is now a recursive macro. When you actually try to use it in code, the preprocessor apparently just bails and doesn't do any text substitution. But, the name of the preprocessor symbol is still defined, so my ifdef check still saw RR_ASSERT existing. Evil.

BTW the thing that kicked this off is that fucking VC x64 doesn't support inline assembly. ARGH YOU COCK ASS. Because of that we had long ago written something like


#ifdef _X86
#define RR_ASSERT_BREAK()  __asm int 3
#else
#define RR_ASSERT_BREAK()  assert(0)
#endif

which is what caused the difficulty.

8 comments:

Jon said...

btw, you can also use the __debugbreak intrinsic.

nothings said...

Except for the fact that we're sharing the code and I develop in VC6.

ryg said...

Well, you can just use __debugbreak for the x64 builds.

VC x64 not supporting inline ASM bit me in a major way recently: I really did need to write some ASM code to interface with code generated for a different calling convention, plus some small SSE tidbits that needed a certain register mapping. Not only is it inconvenient, it also forces me to make some things that are pure implementation helpers for one file public symbols, and in this case it also made me use fucking MASM. I promptly got reminded why I avoided that POS for years when it randomly decided to assemble "cmpneqps xmm8, [rdx + blah]" as "cmpeqps xmm8, [rdx + blah]" in some places. What the hell, people?

cbloom said...

"btw, you can also use the __debugbreak intrinsic. "

Yeah, once I figured out that the problem was a circular define, the solution was easy.

"VC x64 not supporting inline ASM bit me in a major way recently:"

Yeah that's very annoying.

Big McLargeHuge said...

There's a DebugBreak() function in windows.h, you can use that on all platforms and it works with VC6 too.

Arseny Kapoulkine said...

To be more precise, preprocessor substitutes RR_ASSERT(expr) by assert(expr), and then substituted assert by RR_ASSERT. Which is sometimes a useful behavior, i.e.

#define new new(__FILE__, __LINE__)

does a single substitution (yes, I know that this does not work with placement new and that there are better methods).

cbloom said...

"To be more precise, preprocessor substitutes RR_ASSERT(expr) by assert(expr), and then substituted assert by RR_ASSERT. Which is sometimes a useful behavior, i.e."

Oh yeah, you are quite right.

Michael said...

http://xkcd.com/754/

old rants