c++visual-studiodebugginganonymous-functioncallstack

How to read name of anonymous lambdas in Visual Studio call stack?


It's always been challenging to me to track anonymous lambdas in the call stack, so I wrote some sandbox code in order to deal with the issue:

int main() {
  [] {
    std::cout << "Hello World!\n";
  } ();

  [] {
    std::cout << "Hello World!\n";
  } ();
}

Here I'm calling anonymous lambdas in place. I put breakpoints inside the lambdas' bodies and check the call stack. What I see is:

`main'::`2'::<lambda_1>::operator()()
`main'::`2'::<lambda_2>::operator()()

I've mostly managed to find out the meaning of the records by playing around with anonymous lambdas inside different functions or namespaces.

One thing I cannot understand is this number 2 separating the name of the function main and the label of the lambda <lambda_1>. Does anyone know what this number mean?


Solution

  • The `2' is coming from a part of the mangled name that identifies the nesting block scope. Examples (godbolt):

    void foo() 
    {
      [] { int i = 11; } (); // ??R<lambda_1>@?1??foo@@YAXXZ@QEBA@XZ => `foo'::`2'::<lambda_1>::operator()
      [] { int i = 12; } (); // ??R<lambda_2>@?1??foo@@YAXXZ@QEBA@XZ => `foo'::`2'::<lambda_2>::operator()
    
      {
        [] { int i = 21; } (); // ??R<lambda_3>@?2??foo@@YAXXZ@QEBA@XZ => `foo'::`3'::<lambda_3>::operator()
        [] { int i = 21; } (); // ??R<lambda_4>@?2??foo@@YAXXZ@QEBA@XZ => `foo'::`3'::<lambda_4>::operator()
      }
    
      {
        [] { int i = 31; } (); // ??R<lambda_5>@?3??foo@@YAXXZ@QEBA@XZ => `foo'::`4'::<lambda_5>::operator()
        [] { int i = 31; } (); // ??R<lambda_6>@?3??foo@@YAXXZ@QEBA@XZ => `foo'::`4'::<lambda_6>::operator()
    
        {
            [] { int i = 41; } (); // ??R<lambda_7>@?4??foo@@YAXXZ@QEBA@XZ => `foo'::`5'::<lambda_7>::operator()
        }
      }
    
      {
        [] { int i = 51; } (); // ??R<lambda_8>@?5??foo@@YAXXZ@QEBA@XZ => `foo'::`6'::<lambda_8>::operator()
        [] { int i = 52; } (); // ??R<lambda_9>@?5??foo@@YAXXZ@QEBA@XZ => `foo'::`6'::<lambda_9>::operator()
      }
    
      [] { int i = 13; } (); // ??R<lambda_8>@?1??foo@@YAXXZ@QEBA@XZ => `foo'::`2'::<lambda_8>::operator()
    }
    

    So `2' indicates the function level block, and higher numbers enumerate inner blocks. This is just the normal (undocumented) mangling scheme for nested elements used by MSVC. I.e. that is simply the way the compiler names symbols.

    Regarding the mangling scheme, see e.g. this link for some more information. The number `2' itself comes from the ?1 part in ??R<lambda_1>@?1??foo@@YAXXZ@QEBA@XZ. It is an "encoded number". Here, the number 1 in the mangled name becomes 2 in the demangled name (see here). Also, see here for the corresponding code in the LLVM demangler.

    The function level block starts with the mangled number 1 (demangled `2') rather than 0 because there can be a higher level block in special functions, namely initializer lists in constructors. For example (godbolt):

    struct Struct{
        void (*func)();
        Struct(): func(
          // ?<lambda_invoker_cdecl>@<lambda_1_>@?0???0Struct@@QEAA@XZ@SA@XZ 
          // => `Struct::Struct'::`1'::<lambda_1_>::<lambda_invoker_cdecl>
          [](){ int i = 0; }
        ) {}
    };
    void foo() {
      Struct s;
    }
    

    Here you get a mangled 0 and thus a demangled `1'. Also, in function try blocks the mangled level 0 can appear. For example (godbolt):

    void foo() try
    {
    }
    catch(...)
    {
        [](){ int i = 111; }();
    }
    

    Here the disassembly contains the symbol ?catch$0@?0??foo@@YAXXZ@4HA (demangled int `void __cdecl foo(void)'::`1'::catch$0) to indicate the catch. The lambda within the catch however has the mangled nesting level 3 (demangled `4'). (I guess +1 for the function body, +1 for the catch declarator.)