<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Currently, Swift adds a hidden byref error parameter to propagate thrown errors:<div class=""><br class=""></div><div class=""><div class=""></div></div><blockquote type="cite" class=""><div class=""><div class="">public func foo() throws {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>throw FooError.error</div><div class="">}</div></div><div class=""><br class=""></div><div class=""><div class="">define void @_TF4test3fooFzT_T_(%swift.refcounted* nocapture readnone, <b class="">%swift.error** nocapture</b>) #0 {</div><div class="">entry:</div><div class="">&nbsp; %2 = tail call { %swift.error*, %swift.opaque* } @swift_allocError(/* snip */)</div><div class="">&nbsp; %3 = extractvalue { %swift.error*, %swift.opaque* } %2, 0</div><div class="">&nbsp; <b class="">store %swift.error* %3, %swift.error** %1, align 8</b></div><div class="">&nbsp; ret void</div><div class="">}</div></div></blockquote><div class=""><div class=""><br class=""></div><div class="">This means that call sites for throwing functions must always check if an exception occurred. This makes it essentially equivalent to returning an error code in addition to the function's actual return type.</div><div class=""><br class=""></div><div class="">On the other hand, there are exception handling mechanisms where the execution time cost in the success case is zero, and the error case is expensive. When you throw, the runtime walks through the return addresses on the stack to find out if there's an associated catch block that can handle the current exception.&nbsp;<a href="https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#id29" class="">Apple uses this mechanism</a>&nbsp;(with the Itanium C++ ABI) for C++ and Objective-C exceptions, at least on x86_64.</div><div class=""><br class=""></div><div class="">Other compiler engineers, like&nbsp;<a href="http://joeduffyblog.com/2016/02/07/the-error-model/" class="">Microsoft's Joe Duffy</a>, have determined that there actually is a non-trivial cost associated to branching for error codes. In exchange for faster error cases, you get slower success cases. This is mildly unfortunate for throwing functions that overwhelmingly succeed.</div><div class=""><br class=""></div><div class="">As Fernando Rodríguez reports in another thread, you have many options to signal errors right now (I took the liberty to add mechanisms that he didn't cover):</div><div class=""><br class=""></div><div class=""><ul class="MailOutline"><li class="">trapping</li><li class="">returning nil</li><li class="">returning an enum that contains a success case and a bunch of error cases (which is really just a generalization of "returning nil")</li><li class="">throwing</li></ul><div class=""><br class=""></div></div><div class="">With the current implementation, it seems to me that the main difference between throwing and returning an enum is that catch works even when you don't know what you're catching (but I really hope that we can get typed throws for Swift 4, because unless you actually don't know what you're catching, this feels like an anti-feature). However, if throwing and returning an enum had different-enough performance characteristics, the guidance could become:</div><div class=""><br class=""></div><div class=""><ul class="MailOutline"><li class="">return an enum value if you expect that the function will fail often or if recovery is expected to be cheap;</li><li class="">throw if you expect that the function will rarely fail or if recovery is expected to be expensive for at least one failure reason (for example, if you'd have to re-establish a connection after some network error, or if you'd have to start over some UI process because the user picked a file that was deleted before it could be opened).</li></ul></div><div class=""><br class=""></div><div class="">Additionally, using the native ABI to throw means that you can throw across language boundaries, which might be useful in the possible but distant future in which Swift interops with C++. Even though catching from the other language will probably be tedious, that would already be useful in language sandwiches to unwind correctly (like in Swift that calls C++ that calls Swift, where the topmost Swift code throws).</div><div class=""><br class=""></div><div class="">I don't really know what to expect in terms of discussion, especially since it may boil down to "we're experts in this fields and you're just peasants" or "the cost of changing this isn't worth the benefit". Still, I'd like some more insight into why Swift exceptions don't use the same mechanism as C++ exceptions and Objective-C exceptions. The error handling rationale document is&nbsp;<a href="https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#id62" class="">very terse</a>&nbsp;on the implementation design, especially given the length of the rest of the document:</div><div class=""><br class=""></div><div class=""></div><blockquote type="cite" class=""><div class="">Error propagation for the kinds of explicit, typed errors that I've been focusing on should be handled by implicit&nbsp;manual propagation. It would be good to bias the implementation somewhat towards the non-error path,&nbsp;perhaps by moving error paths to the ends of functions and so on, and perhaps even by processing cleanups&nbsp;with an interpretive approach instead of directly inlining that code, but we should not bias so heavily as to&nbsp;seriously compromise performance. In other words, we should not use table-based unwinding.</div></blockquote><div class=""><br class=""></div><div class="">I find the rationale somewhat lacking. I can't pretend that I've measured the impact or frequency of retuning a error objects in Objective-C or Swift, and given my access to source code, I probably couldn't do a comprehensive study. However, as linked above, someone did for Microsoft platforms (for Microsoft-platform-style errors) and found that there is an impact. The way it's phrased here, it feels like this was chosen as a rule of thumb.</div><div class=""><br class=""></div><div class="">Throwing and unwind tables are all over the place in a lot of languages that have exceptions (C++, Java, C#). They throw for a lot of the same reasons that Objective-C frameworks returns errors, and people usually seem content with the performance. Since Swift is co-opting the exception terminology, I think that developers reasonably expect that exceptions will have about the same performance cost as in these other languages.</div><div class=""><br class=""></div><div class="">For binary size concerns, since Swift functions have to annotate whether they throw or not, unless I'm mistaken, there only needs to be exception handler lookup tables for functions that call functions that throw. Java and C# compilers can't really decide that because it's assumed that any call could throw. (C++ has `noexcept` and could do this for the subset of functions that only call `noexcept` functions, but the design requires you to be conscious of what can't throw instead of what can.)</div><div class=""><br class=""></div><div class="">Finally, that error handling rationale doesn't really give any strong reason to use one of the two more verbose error handling solutions (throwing vs returning a complex enum value) over the other.</div><div class="">
<br class="Apple-interchange-newline"><span style="color: rgb(0, 0, 0); font-family: 'Lucida Grande'; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; display: inline !important; float: none;" class="">Félix</span>
</div>
<br class=""></div></body></html>