我有以下示例代码:
@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;
}
打印
1
1
1
2
但我期待
1
2
1
2
如果我将第二个结构的名称更改为不同的名称(例如“B”),则始终按预期工作。
没有出现任何警告,因此很难找到程序无法正常运行的原因。
这是我的无知还是llvm的bug?
最佳答案
这确实看起来是一个 clang bug。 (需要注意的是,没有指定 Objective-C++ 语言的标准,所以什么是“正确”还有些悬而未决)
如果我不编译为可执行代码,而是使用这样的命令生成 LLVM IR
clang -g -std=c++11 -Wall -Wextra localstruct.mm -emit-llvm -S
-set:
方法编译如下,突出显示两个函数指针赋值行:
define internal void @"\01-[S1 set:]"(%0* %self, i8* %_cmd, i8 signext %f) uwtable ssp {
%1 = alloca %0*, align 8
%2 = alloca i8*, align 8
%3 = alloca i8, align 1
store %0* %self, %0** %1, align 8
call void @llvm.dbg.declare(metadata !{%0** %1}, metadata !1490), !dbg !1491
store i8* %_cmd, i8** %2, align 8
call void @llvm.dbg.declare(metadata !{i8** %2}, metadata !1492), !dbg !1491
store i8 %f, i8* %3, align 1
call void @llvm.dbg.declare(metadata !{i8* %3}, metadata !1493), !dbg !1494
%4 = load i8* %3, align 1, !dbg !1495
%5 = icmp ne i8 %4, 0, !dbg !1495
br i1 %5, label %6, label %12, !dbg !1495
; <label>:6 ; preds = %0
%7 = load %0** %1, align 8, !dbg !1497
%8 = load i64* @"OBJC_IVAR_$_S1.fn_", !dbg !1497, !invariant.load !1499
%9 = bitcast %0* %7 to i8*, !dbg !1497
%10 = getelementptr inbounds i8* %9, i64 %8, !dbg !1497
%11 = bitcast i8* %10 to void ()**, !dbg !1497
store void ()* @"_ZN10-[S1 set:]1A1fEv", void ()** %11,对齐 8,!dbg !1497
br label %18, !dbg !1500
; <label>:12 ; preds = %0
%13 = load %0** %1, align 8, !dbg !1501
%14 = load i64* @"OBJC_IVAR_$_S1.fn_", !dbg !1501, !invariant.load !1499
%15 = bitcast %0* %13 to i8*, !dbg !1501
%16 = getelementptr inbounds i8* %15, i64 %14, !dbg !1501
%17 = bitcast i8* %16 to void ()**, !dbg !1501
store void ()* @"_ZN10-[S1 set:]1A1fEv", void ()** %17,对齐 8,!dbg !1501
br label %18
; <label>:18 ; preds = %12, %6
ret void, !dbg !1503
}
这两种情况似乎都引用了相同的函数符号_ZN10-[S1 set:]1A1fEv
。如果您查看结构中该方法的相应代码,它会引用两个:_ZZN2S23setEaEN1A1fEv
和 _ZZN2S23setEaEN1A1fE_0v
。
FWIW,GCC 的 Objective-C++ 编译器产生了所需的结果。请务必向 clang 项目报告该错误,而不要只是解决代码中的问题。
关于objective-c - llvm 从不同范围内定义的两个本地结构定义中选择第一个(错误的)本地结构定义 : is that llvm's bug?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12628892/