When you have a member function with a ref-qualifier, for example:
struct Foo { void Func() &; void Func2() &&; };
clang-cl was not emitting this information. Doing so is a bit awkward, because it's not a property of the LF_MFUNCTION type, which is what you'd expect. Instead, it's a property of the this pointer which is actually an LF_POINTER. This record has an attributes bitmask on it, and our handling of this bitmask was all wrong. We had some parts of the bitmask defined incorrectly, but importantly for this bug, we didn't know about these extra 2 bits that represent the ref qualifier at all. We can see this in the reference implementation here:
https://github.com/Microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1524-L1525
And we can also observe that clang-cl and cl emit different records by compiling a simple program and using llvm-pdbutil to dump the results of each one.
// cat foo.cpp struct A { int NoRefQual(); int RefQual() &; int RefQual() &&; int LValueRef() &; int RValueRef() &&; }; int main(int argc, char **argv) { A *GenericPtr = nullptr; A a; return 0; }
With clang:
$ clang-cl.exe /Z7 /GS- /GR- foo.cpp /o foo-clang.exe $ llvm-pdbutil.exe dump -types -type-index=0x1009 -dependents -type-data foo-clang.pdb Types (TPI Stream) ============================================================ Showing 1 records and their dependents (7 records total) 0x1003 | LF_STRUCTURE [size = 32] `A` unique name: `.?AUA@@` vtable: <no type>, base list: <no type>, field list: <no type> options: forward ref (-> 0x1009) | has unique name, sizeof 0 Bytes ( 0000: 1E000515 00008002 00000000 00000000 00000000 00004100 2E3F4155 41404000 |......................A..?AUA@@.| ) 0x1004 | LF_POINTER [size = 12] referent = 0x1003, mode = pointer, opts = None, kind = ptr64 Bytes ( 0000: 0A000210 03100000 0C000100 |............| ) 0x1005 | LF_ARGLIST [size = 8] Bytes ( 0000: 06000112 00000000 |........| ) 0x1006 | LF_MFUNCTION [size = 28] return type = 0x0074 (int), # args = 0, param list = 0x1005 class type = 0x1003, this type = 0x1004, this adjust = 0 calling conv = cdecl, options = None Bytes ( 0000: 1A000910 74000000 03100000 04100000 00000000 05100000 00000000 |....t.......................| ) 0x1007 | LF_METHODLIST [size = 20] - Method [type = 0x1006, vftable offset = -1, attrs = public] - Method [type = 0x1006, vftable offset = -1, attrs = public] Bytes ( 0000: 12000612 03000000 06100000 03000000 06100000 |....................| ) 0x1008 | LF_FIELDLIST [size = 80] - LF_ONEMETHOD [name = `NoRefQual`] type = 0x1006, vftable offset = -1, attrs = public Bytes ( 0000: 03000610 00004E6F 52656651 75616C00 |......NoRefQual.| ) - LF_METHOD [name = `RefQual`, # overloads = 2, overload list = 0x1007] Bytes ( 0000: 02000710 00005265 66517561 6C00 |......RefQual.| ) - LF_ONEMETHOD [name = `LValueRef`] type = 0x1006, vftable offset = -1, attrs = public Bytes ( 0000: 03000610 00004C56 616C7565 52656600 |......LValueRef.| ) - LF_ONEMETHOD [name = `RValueRef`] type = 0x1006, vftable offset = -1, attrs = public Bytes ( 0000: 03000610 00005256 616C7565 52656600 |......RValueRef.| ) Bytes ( 0000: 4E000312 11150300 06100000 4E6F5265 66517561 6C00F2F1 0F150200 07100000 |N...........NoRefQual...........| 0020: 52656651 75616C00 11150300 06100000 4C56616C 75655265 6600F2F1 11150300 |RefQual.........LValueRef.......| 0040: 06100000 5256616C 75655265 6600F2F1 |....RValueRef...| ) 0x1009 | LF_STRUCTURE [size = 32] `A` unique name: `.?AUA@@` vtable: <no type>, base list: <no type>, field list: 0x1008 options: has unique name, sizeof 1 Bytes ( 0000: 1E000515 05000002 08100000 00000000 00000000 01004100 2E3F4155 41404000 |......................A..?AUA@@.| )
If we follow the type indices here, we can see that RValueRef, LValueRef, both overloads of RefQual and also NoRefQual all have the same LF_MFUNCTION type, which is 0x1006. Since they have the same LF_MFUNCTION, they also have the same LF_POINTER for the this pointer since it is part of that record. For the sake of comparison later, the dump of this pointer record is:
referent = 0x1003, mode = pointer, opts = None, kind = ptr64 0000: 0A000210 03100000 0C000100
With MSVC
$ cl.exe /Z7 /GS- /GR- foo.cpp /o foo-cl.exe $ llvm-pdbutil.exe dump -types -type-index=0x100E -dependents -type-data foo-cl.pdb Types (TPI Stream) ============================================================ Showing 1 records and their dependents (11 records total) 0x1003 | LF_STRUCTURE [size = 32] `A` unique name: `.?AUA@@` vtable: <no type>, base list: <no type>, field list: <no type> options: forward ref (-> 0x100E) | has unique name, sizeof 0 Bytes ( 0000: 1E000515 00008002 00000000 00000000 00000000 00004100 2E3F4155 41404000 |......................A..?AUA@@.| ) 0x1005 | LF_POINTER [size = 12] referent = 0x1003, mode = pointer, opts = const, kind = ptr64 Bytes ( 0000: 0A000210 03100000 0C040100 |............| ) 0x1006 | LF_ARGLIST [size = 8] Bytes ( 0000: 06000112 00000000 |........| ) 0x1007 | LF_MFUNCTION [size = 28] return type = 0x0074 (int), # args = 0, param list = 0x1006 class type = 0x1003, this type = 0x1005, this adjust = 0 calling conv = cdecl, options = None Bytes ( 0000: 1A000910 74000000 03100000 05100000 00000000 06100000 00000000 |....t.......................| ) 0x1008 | LF_POINTER [size = 12] referent = 0x1003, mode = pointer, opts = const, kind = ptr64 Bytes ( 0000: 0A000210 03100000 0C042100 |..........!.| ) 0x1009 | LF_MFUNCTION [size = 28] return type = 0x0074 (int), # args = 0, param list = 0x1006 class type = 0x1003, this type = 0x1008, this adjust = 0 calling conv = cdecl, options = None Bytes ( 0000: 1A000910 74000000 03100000 08100000 00000000 06100000 00000000 |....t.......................| ) 0x100A | LF_POINTER [size = 12] referent = 0x1003, mode = pointer, opts = const, kind = ptr64 Bytes ( 0000: 0A000210 03100000 0C041100 |............| ) 0x100B | LF_MFUNCTION [size = 28] return type = 0x0074 (int), # args = 0, param list = 0x1006 class type = 0x1003, this type = 0x100A, this adjust = 0 calling conv = cdecl, options = None Bytes ( 0000: 1A000910 74000000 03100000 0A100000 00000000 06100000 00000000 |....t.......................| ) 0x100C | LF_METHODLIST [size = 20] - Method [type = 0x1009, vftable offset = -1, attrs = public] - Method [type = 0x100B, vftable offset = -1, attrs = public] Bytes ( 0000: 12000612 03000000 09100000 03000000 0B100000 |....................| ) 0x100D | LF_FIELDLIST [size = 80] - LF_ONEMETHOD [name = `NoRefQual`] type = 0x1007, vftable offset = -1, attrs = public Bytes ( 0000: 03000710 00004E6F 52656651 75616C00 |......NoRefQual.| ) - LF_METHOD [name = `RefQual`, # overloads = 2, overload list = 0x100C] Bytes ( 0000: 02000C10 00005265 66517561 6C00 |......RefQual.| ) - LF_ONEMETHOD [name = `LValueRef`] type = 0x100B, vftable offset = -1, attrs = public Bytes ( 0000: 03000B10 00004C56 616C7565 52656600 |......LValueRef.| ) - LF_ONEMETHOD [name = `RValueRef`] type = 0x1009, vftable offset = -1, attrs = public Bytes ( 0000: 03000910 00005256 616C7565 52656600 |......RValueRef.| ) Bytes ( 0000: 4E000312 11150300 07100000 4E6F5265 66517561 6C00F2F1 0F150200 0C100000 |N...........NoRefQual...........| 0020: 52656651 75616C00 11150300 0B100000 4C56616C 75655265 6600F2F1 11150300 |RefQual.........LValueRef.......| 0040: 09100000 5256616C 75655265 6600F2F1 |....RValueRef...| ) 0x100E | LF_STRUCTURE [size = 32] `A` unique name: `.?AUA@@` vtable: <no type>, base list: <no type>, field list: 0x100D options: has unique name, sizeof 1 Bytes ( 0000: 1E000515 05000002 0D100000 00000000 00000000 01004100 2E3F4155 41404000 |......................A..?AUA@@.| )
Here we see several different LF_MFUNCTION records in use.
0x1009: Used by Foo::RValueRef and one overload of Foo::RefQual
0x100B: Used by Foo::LValueRef and one overload of Foo::RefQual
0x1007: Used by Foo::NoRefQual
And the only difference between any of them is the "this type" field. So for this pointer types, we have:
0x1008: Used by Foo::RValueRef and one overload of Foo::RefQual
0x100A: Used by Foo::LValueRef and one overload of Foo::RefQual
0x1005: Used by Foo::NoRefQual
And if we then go look at the record data of each one, we have:
0x1008: referent = 0x1003, mode = pointer, opts = const, kind = ptr64 0000: 0A000210 03100000 0C042100 0x100A: referent = 0x1003, mode = pointer, opts = const, kind = ptr64 0000: 0A000210 03100000 0C041100 0x1005: referent = 0x1003, mode = pointer, opts = const, kind = ptr64 0000: 0A000210 03100000 0C040100
The data that llvm-pdbutil looks identical for each one, but if we look closer at the record bytes we can see that there is 1 bit difference in each of them.
After this patch, clang-cl emits compatible debug info for this case.
0x1009: referent = 0x1003, mode = pointer, opts = None, kind = ptr64 0000: 0A000210 03100000 0C002100 0x1007: referent = 0x1003, mode = pointer, opts = None, kind = ptr64 0000: 0A000210 03100000 0C001100 0x1004: referent = 0x1003, mode = pointer, opts = None, kind = ptr64 0000: 0A000210 03100000 0C000100