I have following example code:
@interface S1 : NSObject
{
void(*fn_)();
}
@end
@implementation S1
- (void) set:(BOOL)f
{
if (f)
{
struct A { static void f() { std::cout << "1" << std::endl; } };
fn_ = A::f;
}
else
{
struct A { static void f() { std::cout << "2" << std::endl; } };
fn_ = A::f;
}
}
- (void) test { fn_(); }
@end
struct S2
{
void set(BOOL f)
{
if (f)
{
struct A { static void f() { std::cout << "1" << std::endl; } };
fn_ = A::f;
}
else
{
struct A { static void f() { std::cout << "2" << std::endl; } };
fn_ = A::f;
}
}
void test() { fn_(); }
void(*fn_)();
};
int main(int argc, const char * argv[])
{
auto s1 = [[S1 alloc] init];
[s1 set:TRUE];
[s1 test];
[s1 set:FALSE];
[s1 test];
S2 s2;
s2.set(TRUE);
s2.test();
s2.set(FALSE);
s2.test();
return 0;
}
It prints
1
1
1
2
but I expecting
1
2
1
2
If I change name of second struct to different (e. g. “B”), always works as expected.
No warnings appears, so it’s hard to find why your program doesn’t work properly.
Is that my ignorance or llvm’s bug?
It would indeed appear to be a clang bug. (with the caveat that there is no standard specifying the Objective-C++ language, so what is “correct” is a little bit up in the air)
If instead of compiling to executable code, I generate LLVM IR, using a command like this
the
-set:method compiles down to this, with what would appear to be the two function pointer assignment lines highlighted:store void ()* @"_ZN10-[S1 set:]1A1fEv", void ()** %11, align 8, !dbg !1497store void ()* @"_ZN10-[S1 set:]1A1fEv", void ()** %17, align 8, !dbg !1501Both cases appear to reference the same function symbol
_ZN10-[S1 set:]1A1fEv. If you look at the corresponding code for the method in the struct, it references two:_ZZN2S23setEaEN1A1fEvand_ZZN2S23setEaEN1A1fE_0v.FWIW, GCC’s Objective-C++ compiler produces the desired result. Please do report the bug to the clang project and don’t just work around the problem in your code.