I dug a little further into what you tried to do with catch and where I think it went wrong. You can see my thoughts in the notes I added in D3263. It could even be possible to fix the problem for real by changing the DmdRes domain again. Originally, we broke it up into Diverges and Dunno. You added an additional ThrowsExn to mean "diverges or throws an exception". But I think a critical point in the domain, for this analysis, is "definitely does not throw an exception". If we have
catch# m f s
then we can speculatively evaluate/unbox only those expressions in (m s) that definitely don't throw exceptions (whether they succeed or diverge).
One other thought: it might be worth considering a catchIO# primitive. This would catch exceptions thrown by raiseIO# but *not* by raise#. This could be made *much* stricter, I believe, if give raiseIO# a special place in the result domain.