This is PR36536.
There are a few ways to reach Sema::ActOnStartOfFunctionDef with a null
Decl. Currently, the parser continues on to attempt to parse the
statements in the function body without pushing a function scope or
declaration context. However, lots of statement parsing logic relies on
getCurFunction() returning something reasonable. It turns out that
getCurFunction() will never return null today because of an optimization
where Sema pre-allocates one FunctionScopeIfno and reuses it when
possible. This goes wrong when something inside the function body causes
us to push another function scope, such as requiring an implicit
definition of a special member function. Reusing the state clears it
out, which will lead to bugs. In PR36536, we found that the SwitchStack
gets unbalanced, because we push a switch, clear out the stack, and then
try to pop a switch that isn't there.
As a follow-up, I plan to move the pre-allocated FunctionScopeInfo out
of the FunctionScopes stack. This means the FunctionScopes stack will
often be empty, which will make getCurFunction() assert. That's a
high-risk change, so I want to separate it out.
Feels a bit long-term risky since ActOnStartOfFunctionDef() and ActOnFinishFunctionBody() both need to know about this special-case invariant. Maybe it's worth to add a FakeFunctionScopeCount member to sema in +assert builds, and to increment that here, assert it's > 0 in the other place and decrement it there, and then assert it's 0 at end of TU?