This sort of roughly corresponds with my feeling. It’s clear that using an explicit stack is more efficient. But often this explicit stack comes at a price: harder to read code, effort to refactor, etc.
Anyway, I think the best thing is that I will do some experiments both ways. I want to see if there is an easy way to isolate out the part of MIR construction that is overflowing just now and use an explicit stack. However, even if I do that, it will just mean that the code is “no worse” than the rest of the compiler, most of which uses recursive algorithms. I assume that if you write a torture test with a deeply nested match, for example, you will eventually blow the stack in typeck and elsewhere. MIR happens to behave differently here because it uses recursion to model the scopes, and each let introduces a new scope (most other parts of the compiler handle lets more imperatively).