As far as my experience goes, a breakpoint on a given line of code means that the first time the debugger breaks on that line, nothing of that line has been executing yet. Or, in other words, when the debugger stops at a breakpoint, it stops there before executing that line of code, not after it or in the middle of it.
And I don't think I had ever seen this view to be contraddicted (despite my experience is mostly on C++, and so that's what I've been always debugging).
However, this seems not to be the case when coroutines are involved.
Take this code from a previous question of mine (it is broken because the code has UB, but the issue I'm describing occurs before that is triggered, so it doesn't matter), a target excerpt of which is here:
class [[nodiscard]] CoroTaskSub {
…
public:
…
CoroTaskSub(auto h)
: hdl{h} {
}
…
};
CoroTaskSub coro() {
…
}
CoroTaskSub callCoro() {
std::cout << " callCoro(): CALL coro()\n";
co_await coro();
…
}
I've done the following:
- put a breakpoint at the
cout
line - put a breakpoint at the
co_await
line - run the program (at this point the debugger stops at the first of those two breakpoints)
- put a breakpoint at the line
: hdl{h} {
, i.e. in the constructor of theCoroTaskSub
class¹ - continue with the program
At this point I expected that the debugger woudl break at the breakpoint on the co_await
line, before anything of that line is evaluated, but instead it breaks a the breakpoint on CoroTaskSub
's constructor, just like the coro()
is being processed already.
That's a bit weird, no?
Do you know why that happens?
(¹) Because of how the program is structured, the control has already passed once here, upon the call callCoro()
that is done in main
, but I've put the breakpoint only here because I wanted to see when this constructor is called to construct coro()
's CoroTaskSub
.