2013-04-03 20 views
14

Werfen Sie einen Blick auf dieses Stück Code:Seltsam Clang Verhalten

#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; 
} 

es mit

g++ -o test test.cpp -std=c++11 

führt zu kompilieren:

bla 

wie es tun sollten. Kompilieren mit

clang++ -o test test.cpp -std=c++11 -stdlib=libc++ 

führt zu:

zsh: illegal hardware instruction ./test 

Und es mit

clang++ -o test test.cpp -std=c++11 -stdlib=stdlibc++ 

Kompilieren führt auch zu:

zsh: illegal hardware instruction ./test 

Clang/GCC-Versionen:

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) 

Wer irgendwelche Vorschläge was schief geht?

Vielen Dank im Voraus!

+9

Ich würde sagen, das ist ein Bug in Clang –

+0

FYI, mehr Informationen über [ud2 und kling] (http://stackoverflow.com/q/26309300/1708801) –

Antwort

5

Dies ist höchstwahrscheinlich ein Bug im Klang 3.2. Ich kann den Unfall mit dem Klunkerstamm nicht reproduzieren.

+0

Affirmative. Trunk scheint gut zu funktionieren. Also sollte es wahrscheinlich in clang 3.3 behoben werden. – rralf

11

Ja, es ist ein Bug in Clang ++. Ich kann es mit CLang 3.2 in i386-pc-linux-gnu reproduzieren.

Und jetzt einige zufällige Analyse ...

ich, dass der Fehler in der Umwandlung von labmda auf Zeiger-to-Funktion gefunden haben: der Compiler erzeugt eine Art mit der entsprechenden Signatur thunk dass ruft das Lambda auf, aber es hat die Anweisung ud2 anstelle von ret.

Die Anweisung ud2, wie Sie wahrscheinlich alle wissen, ist eine Anweisung, die explizit die Ausnahme "ungültiger Opcode" auslöst. Das heißt, eine Anweisung wurde absichtlich nicht definiert.

einen Blick auf die disassemble nehmen: Das ist die Thunk-Funktion:

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...!!! 

wird also sein, ein minimales Beispiel für den Fehler einfach:

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

Merkwürdigerweise hat der Fehler nicht Wenn der Rückgabetyp ein einfacher Typ ist, z. B. int. Dann wird die erzeugte thunk ist:

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 

Ich vermute, dass das Problem in der Weiterleitung des Rückgabewert ist. Wenn es in ein Register passt, wie eax geht alles gut. Aber wenn es eine große Struktur ist, wie std::string es im Stapel zurückgegeben wird, ist der Compiler verwirrt und gibt die ud2 in Verzweiflung.