C lacks "zero-cost" exceptions, where the code should be just as fast as it would be if no exceptions were involved as long as one isn't thrown. Although they are much more expensive when thrown than checking a return value and using indirection for any logical return values, they are a bit cheaper when you don't expect the exception to happen very often, or don't care about performance if it does (e.g. a corrupt png or jpeg file).
My dream is something like the UX of Swift's exceptions (basically normal C error handling except for it does all the tedious pointer work for you) with the ability to tell the compiler to either implement the exceptions as return values or stack unwinds for a particular code path with the flick of a switch.
My big hope in this regard is Rust, which has a very good track record if implementing good, usable, zero-cost abstractions.
If they manage to implement zero-cost futures [1][2], maybe they will also implement sime kind of zero-cost error handling abstraction (either via exceptions, or via some other useful abstraction for that purpose).
Rust's abstractions are only zero cost if you're looking in the wrong place for your costs. The Result / try! stuff in particular I would be very surprised if it is faster than a good exception approach, for example, particularly the happy path. It's amortised throughout though, while different exception throwing mechanisms have dramatically different performance, so people's intuitions aren't reliable.
I like Rust a lot. Zero costs is not a reason though; my reason is no GC and not as bad as C or C++.
Transporting and checking for errors throughout the call graph definitely isn't free. Simple exception unwinding isn't very expensive either - store handler info on the stack and use stack frames and it's just a linked list search. Keep popping until you're done. Freeing locals in a way compatible with the language semantics along the way is the main tricky bit. But you have that cost with error results too, it's not extra.
The real cost is that exceptions force the compiler to be pessimistic and pedantically cope with exceptions everywhere. I really don't think there's much win at all, compared to that, in return values the compiler forces you to check. And if you skip the checking, that's a different kind of pain.
Checked exceptions is a different but related story. I think they're a fine idea in the small but appallingly bad in the large; when glueing together 30 different libraries with their own exception types, everything end up throwing anything, particularly when some higher level abstractions have been applied, like iterator streams or monads. Forced return code error checking like Rust's is isomorphic with checked exceptions. It is my biggest worry for the language.
That is, the problem isn't performance - the isomorphism shows that there is no necessary difference when rules are consistently applied - the problem is Java was a good demonstration the checked exceptions were misguided.
Rust's try macro will automatically use any available conversions when moving exceptions up the chain, so if you want to wrap exception A in B this can be done without pain. Rust also relegates a lot of what would have been exceptions in Java to panics, so that things like logic errors or issues with arguments can be kept from complicating error handling.
These alleviate a lot of what made Java's checked exceptions so unhelpful, so I don't think you can really conflate the two.
My dream is something like the UX of Swift's exceptions (basically normal C error handling except for it does all the tedious pointer work for you) with the ability to tell the compiler to either implement the exceptions as return values or stack unwinds for a particular code path with the flick of a switch.