It took me forever to track down that there was a bug in my code being triggered by /OPT:ICF
:
Because /OPT:ICF can cause the same address to be assigned to different functions or read-only data members (const variables compiled by using /Gy), it can break a program that depends on unique addresses for functions or read-only data members.
(I had been storing and comparing function pointers for equality, which breaks when the linker throws away identical functions.)
Now I need to find every place where I might have done such a thing.
The test case is of course trivial:
//MSVC: /Gy /link /OPT:ICF
int test1(void) { return 0; }
int test2(void) { return 0; }
int main(void) { return test1 == test2; }
I've tried -Wall
, -Wextra
, -Weverything
, -pedantic
, etc. but none of them generate warnings.
Is there any compiler option or tool (whether part of Visual C++, GCC, Clang, or other) that can analyze my code and tell me where I'm comparing function pointers with each other, like in the code above?
Is there any compiler option or tool (whether part of Visual C++, GCC, Clang, or other) that can analyze my code and tell me where I'm comparing function pointers with each other, like in the code above?
I'm not sure if there exists such a compiler option.
However, there is such a tool. clang-tidy. You can write your own checks for clang-tidy, it's actually remarkably easy if you follow this blog. Specifically, the AST comes with a bunch of matchers already, which should handle the use-case you want.
Something like this seems to work:
binaryOperator(
anyOf(hasOperatorName("=="), hasOperatorName("!=")),
hasLHS(ignoringImpCasts(declRefExpr(hasType(functionType())))),
hasRHS(ignoringImpCasts(declRefExpr(hasType(functionType())))))
Which flags the example in the OP:
fp.cxx:3:25: note: "root" binds here
int main(void) { return test1 == test2; }
^~~~~~~~~~~~~~
That works specifically for the OP case, but you actually have to be more explicit to match all the other likely cases:
const auto AnyFunc = ignoringImpCasts(declRefExpr(hasType(anyOf(
functionType(),
pointsTo(functionType()),
references(functionType())))));
Finder->AddMatcher(binaryOperator(
anyOf(hasOperatorName("=="), hasOperatorName("!=")),
hasLHS(AnyFunc),
hasRHS(AnyFunc)).bind("op"), this);
Or something close to that effect.