奇怪的铿锵行为

rra*_*alf 14 c++ g++ llvm clang c++11

看看这段代码:

#include <iostream>
#include <string>

void foo(int(*f)()) {
    std::cout << f() << std::endl;
}

void foo(std::string(*f)()) {
    std::string s = f();
    std::cout << s << std::endl;
}

int main() {
    auto bar = [] () -> std::string {
        return std::string("bla");
    };

    foo(bar);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

用它编译

g++ -o test test.cpp -std=c++11
Run Code Online (Sandbox Code Playgroud)

导致:

bla
Run Code Online (Sandbox Code Playgroud)

喜欢它应该做的.用它编译

clang++ -o test test.cpp -std=c++11 -stdlib=libc++
Run Code Online (Sandbox Code Playgroud)

导致:

zsh: illegal hardware instruction  ./test
Run Code Online (Sandbox Code Playgroud)

并用它编译

clang++ -o test test.cpp -std=c++11 -stdlib=stdlibc++
Run Code Online (Sandbox Code Playgroud)

还导致:

zsh: illegal hardware instruction  ./test
Run Code Online (Sandbox Code Playgroud)

Clang/GCC版本:

clang version 3.2 (tags/RELEASE_32/final)
Target: x86_64-pc-linux-gnu
Thread model: posix

gcc version 4.7.2 (Gentoo 4.7.2-r1 p1.5, pie-0.5.5) 
Run Code Online (Sandbox Code Playgroud)

有人提出任何建议是什么问题吗?

提前致谢!

rod*_*igo 11

是的,这是Clang ++中的一个错误.我可以用i386-pc-linux-gnu中的CLang 3.2重现它.

现在进行一些随机分析......

我发现这个bug是从labmda到指向函数的转换:编译器创建了一种带有调用lambda的适当签名的thunk,但它有指令ud2而不是ret.

指令ud2,大家都可能知道,就是明确提出了"无效操作"异常的指令.也就是说,一条指令故意未定义.

看一下反汇编:这是thunk函数:

main::$_0::__invoke():
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        movl    8(%ebp), %eax
        movl    %eax, (%esp)
        movl    %ecx, 4(%esp)
        calll   main::$_0::operator()() const ; this calls to the real lambda
        subl    $4, %esp
        ud2   ; <<<-- What the...!!!
Run Code Online (Sandbox Code Playgroud)

所以这个bug的最小例子就是:

int main() {
    std::string(*f)() = [] () -> std::string {
        return "bla";
    };
    f();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

奇怪的是,如果返回类型是一个简单类型,则不会发生错误,例如int.然后生成的thunk是:

main::$_0::__invoke():
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        movl    %eax, (%esp)
        calll   main::$_0::operator()() const
        addl    $8, %esp
        popl    %ebp
        ret
Run Code Online (Sandbox Code Playgroud)

我怀疑问题在于转发返回值.如果它适合寄存器,例如eax一切顺利.但是如果它是一个很大的结构,比如std::string它在堆栈中返回,那么编译器就会感到困惑并ud2在绝望中发出.


d0k*_*d0k 5

这很可能是clang 3.2中的一个错误.我不能用clang trunk重现崩溃.