I have doubts about whether "real" projects generally have these kinds of concerns. But even if they do, you can clean them up in a `finally`, or your language's equivalent.
`finally`/RAII doesn’t depend on checked exceptions. It always runs (short of something that aborts the program without unwinding the call stack; and in those cases single-exit won’t save you).
Mixing finally and RAII into the same category doesn't really make sense here.
And I'm not saying that finally depends on checked exceptions, I'm saying that pattern is very brittle. You change lower code to throw a new exception, and you change the above code to catch it like you're supposed to, but now the middle code has no idea that there's this new exception and leaks resources. So you end up either having brittle code who's correctness depends on implicit choices of the code around it, or you're wrapping pretty much all function bodies with try-catch-finally-rethrow.
I find it a good rule of thumb to allocate and deallocate a resource at the same level in my design, and to maintain any associated handle at that level as well. I have encountered few situations where this didn’t work well, and almost all of them arose because an external module introduced some form of unavoidable asymmetry at its interface.
If you’re able to follow that pattern then safe handling of unusual control flows tends to follow naturally. You just adopt whatever mechanism your language provides for scheduling the code to clean up and release resources at the same time as you allocate those resources. Almost all mainstream languages at a higher level than C provide a suitable mechanism today: RAII, some variation of `with` or `using` block, try-finally, Lispy macros, Haskelly monad infrastructure, etc.
That way, the only places you need to write any extra logic are the layers where you allocate/deallocate and possibly lower layers that manage side effects on those resources in the unusual case that they require a specific form of recovery beyond the default behaviour in the event of aborting early.
Hopefully if you do have to work at low level like C or assembly language and you’re using forms of control flow that could bypass clean-up logic then you already have at least some coding standards established for how to manage resources safely as well.
> middle code has no idea that there's this new exception and leaks resources
You misunderstand how `finally` works. The whole point is that it runs regardless of exception type. So the resulting code in the middle layer doesn’t need to know anything about the code it calls, and doesn’t leak resources, even when calling or called code changes: It’s not brittle.
> or you're wrapping pretty much all function bodies with try-catch-finally-rethrow.
No need for `catch` and rethrowing. And in the case of C# and modern Java, no need for the rest either: You only need to wrap resources that you allocate into `using` (C#) or `try` (Java; but not `try…finally`! [1]). Sure, it’s more verbose than RAII in other languages with automatic storage duration (like C++). But it effectively performs the same.