Details
-
Bug
-
Resolution: Invalid
-
Not Evaluated
-
None
-
5.6.1, 5.11.2
-
None
-
Windows 10 x64
Microsoft Visual Studio 2017 Win64 15.9.4
Microsoft Visual C++ Compiler 19.16.27025.1
Description
I'm experiencing an obscure behavior of Microsoft Visual C++ Compiler on Windows 10 in Debug Configuration.
I couldn't get rid of the QString entity in the example, cause in the other circumstances it is not reproducible or it is merely hidden from the eyes.
Here is a test code snippet where the issue manifests:
#include <QtCore/QString> struct MyQString: QString { using QString::QString; int m_b = 42; }; struct TestString { operator MyQString() { return {}; } }; int takeTwo(QString const & a1, QString const & a2) { return a2.size(); } TestString ts; int good1 = takeTwo(ts.operator MyQString(), QString("Foo")); int good2 = takeTwo(static_cast<MyQString>(ts), QString("Bar")); int bad = takeTwo(ts, QString("Baz"));
When initializing the bad variable within the scope of takeTwo, the a2 argument gets corrupted i.e. its QString::d member (pointer) obtains value 0x2a (42). Since the values are passed by reference, the issue hides in that how the compiler puts the rvalues of the arguments to be passed in takeTwo in the stack. The compiler computes the arguments from right to left (see the disassembly below), and in this case the left argument's stack memory somehow overlaps the memory of the right one. The last wins.
I inspected the Disassembly view at each takeTwo call and found out that the bad one has differences in the stack pointer offsets.
Posting here the takeTwo call disassemblies
; int good1 = takeTwo(ts.operator MyQString(), QString("Foo")); 00007FF8B1311B50 sub rsp,68h 00007FF8B1311B54 mov qword ptr [rsp+48h],0FFFFFFFFFFFFFFFEh 00007FF8B1311B5D lea rdx,[string "Foo" (07FF8B16A4E9Ch)] 00007FF8B1311B64 lea rcx,[rsp+40h] 00007FF8B1311B69 call qword ptr [__imp_QString::QString (07FF8B1682898h)] 00007FF8B1311B6F mov qword ptr [rsp+20h],rax 00007FF8B1311B74 mov rax,qword ptr [rsp+20h] 00007FF8B1311B79 mov qword ptr [rsp+30h],rax 00007FF8B1311B7E lea rdx,[rsp+50h] 00007FF8B1311B83 lea rcx,[ts (07FF8B1783640h)] 00007FF8B1311B8A call TestString::operator MyQString (07FF8B141F9A0h) 00007FF8B1311B8F mov qword ptr [rsp+28h],rax 00007FF8B1311B94 mov rax,qword ptr [rsp+28h] 00007FF8B1311B99 mov qword ptr [rsp+38h],rax 00007FF8B1311B9E mov rdx,qword ptr [rsp+30h] 00007FF8B1311BA3 mov rcx,qword ptr [rsp+38h] 00007FF8B1311BA8 call takeTwo (07FF8B13F57E0h) 00007FF8B1311BAD mov dword ptr [good1 (07FF8B1783644h)],eax 00007FF8B1311BB3 lea rcx,[rsp+50h] 00007FF8B1311BB8 call MyQString::~MyQString (07FF8B141C5D0h) 00007FF8B1311BBD nop 00007FF8B1311BBE lea rcx,[rsp+40h] 00007FF8B1311BC3 call qword ptr [__imp_QString::~QString (07FF8B1682890h)] 00007FF8B1311BC9 add rsp,68h 00007FF8B1311BCD ret
; int good2 = takeTwo(static_cast<MyQString>(ts), QString("Bar")); 00007FF8B1311BD0 sub rsp,68h 00007FF8B1311BD4 mov qword ptr [rsp+48h],0FFFFFFFFFFFFFFFEh 00007FF8B1311BDD lea rdx,[string "Bar" (07FF8B16A4EA0h)] 00007FF8B1311BE4 lea rcx,[rsp+40h] 00007FF8B1311BE9 call qword ptr [__imp_QString::QString (07FF8B1682898h)] 00007FF8B1311BEF mov qword ptr [rsp+20h],rax 00007FF8B1311BF4 mov rax,qword ptr [rsp+20h] 00007FF8B1311BF9 mov qword ptr [rsp+30h],rax 00007FF8B1311BFE lea rdx,[rsp+50h] 00007FF8B1311C03 lea rcx,[ts (07FF8B1783640h)] 00007FF8B1311C0A call TestString::operator MyQString (07FF8B141F9A0h) 00007FF8B1311C0F mov qword ptr [rsp+28h],rax 00007FF8B1311C14 mov rax,qword ptr [rsp+28h] 00007FF8B1311C19 mov qword ptr [rsp+38h],rax 00007FF8B1311C1E mov rdx,qword ptr [rsp+30h] 00007FF8B1311C23 mov rcx,qword ptr [rsp+38h] 00007FF8B1311C28 call takeTwo (07FF8B13F57E0h) 00007FF8B1311C2D mov dword ptr [good2 (07FF8B1783648h)],eax 00007FF8B1311C33 lea rcx,[rsp+50h] 00007FF8B1311C38 call MyQString::~MyQString (07FF8B141C5D0h) 00007FF8B1311C3D nop 00007FF8B1311C3E lea rcx,[rsp+40h] 00007FF8B1311C43 call qword ptr [__imp_QString::~QString (07FF8B1682890h)] 00007FF8B1311C49 add rsp,68h 00007FF8B1311C4D ret
; int bad = takeTwo(ts, QString("Baz")); 00007FF8B1311A60 sub rsp,68h 00007FF8B1311A64 mov qword ptr [rsp+50h],0FFFFFFFFFFFFFFFEh 00007FF8B1311A6D lea rdx,[string "Baz" (07FF8B16A4EA4h)] 00007FF8B1311A74 lea rcx,[rsp+48h] 00007FF8B1311A79 call qword ptr [__imp_QString::QString (07FF8B1682898h)] 00007FF8B1311A7F mov qword ptr [rsp+20h],rax 00007FF8B1311A84 mov rax,qword ptr [rsp+20h] 00007FF8B1311A89 mov qword ptr [rsp+30h],rax 00007FF8B1311A8E lea rdx,[rsp+40h] 00007FF8B1311A93 lea rcx,[ts (07FF8B1783640h)] 00007FF8B1311A9A call TestString::operator MyQString (07FF8B141F9A0h) 00007FF8B1311A9F mov qword ptr [rsp+28h],rax 00007FF8B1311AA4 mov rax,qword ptr [rsp+28h] 00007FF8B1311AA9 mov qword ptr [rsp+38h],rax 00007FF8B1311AAE mov rdx,qword ptr [rsp+30h] 00007FF8B1311AB3 mov rcx,qword ptr [rsp+38h] 00007FF8B1311AB8 call takeTwo (07FF8B13F57E0h) 00007FF8B1311ABD mov dword ptr [bad (07FF8B1783658h)],eax 00007FF8B1311AC3 lea rcx,[rsp+40h] 00007FF8B1311AC8 call MyQString::~MyQString (07FF8B141C5D0h) 00007FF8B1311ACD nop 00007FF8B1311ACE lea rcx,[rsp+48h] 00007FF8B1311AD3 call qword ptr [__imp_QString::~QString (07FF8B1682890h)] 00007FF8B1311AD9 add rsp,68h 00007FF8B1311ADD ret
Observe the lea instructions just before the __imp_QString::QString call. In the bad test it differs by 8 bytes in comparison with the good tests! I suspect that's the reason why the 2nd arg gets ruined.
To me it looks mysterious because for the compiler it makes difference if there is implicit or explicit conversion to QString, as it affects the final result.
Also posting here the cl command line from QtCreator output:
cl -c -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -EHsc /Fddebug\TestQtProject.vc.pdb -DUNICODE -D_UNICODE -DWIN32 -DWIN64 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_CORE_LIB -I..\TestQtProject -I. -I..\..\..\..\Qt\5.11.2\msvc2017_64\include -I..\..\..\..\Qt\5.11.2\msvc2017_64\include\QtCore -Idebug -I..\..\..\..\Qt\5.11.2\msvc2017_64\mkspecs\win32-msvc -Fodebug\ @C:\Users\USE~1\AppData\Local\Temp\main.obj.13544.0.jom main.cpp
I've tested the issue on the other PC in Visual Studio 2017 15.9.3 and ran into the same issue. But on yet another PC in Visual Studio 2017 Win64 15.7.6 and even Visual Studio 2015 it is not reproducible.
Naturally, I'd have posted all this to Microsoft if I could omit the QString dependency. Probably I have missed some subtle detail, but spent a day to figure out the reason why the sfw is crashing in Debug.