<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=""><br class=""><div><blockquote type="cite" class=""><div class="">On 22 Feb 2017, at 21:13, Joe Groff <<a href="mailto:jgroff@apple.com" class="">jgroff@apple.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class=""><br class="Apple-interchange-newline">On Feb 21, 2017, at 8:50 PM, Chris Lattner via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class="">On Feb 20, 2017, at 11:12 PM, John McCall <<a href="mailto:rjmccall@apple.com" class="">rjmccall@apple.com</a>> wrote:<br class=""><blockquote type="cite" class=""><blockquote type="cite" class="">As you know, I still think that adding typed throws is the right thing to do. I understand your concern about “the feature could be misused” but the same thing is true about many other language features.<br class=""></blockquote><br class="">That's fair, but I do think there's an important difference here. The way I see it, typed-throws is really something of an expert feature, not because it's at all difficult to use, but the reverse: because it's easy to use without really thinking about the consequences. (And the benefits are pretty subtle, too.) I'm not saying that we should design it to be hard to use, but I think maybe it shouldn't immediately suggest itself, and it especially shouldn't come across as just a more specific version of throws.<br class=""></blockquote><br class="">Yeah, I agree that it will be appealing to people who don’t know better, but here’s the thing: the (almost certain) Swift design will prevent the bad thing from happening in practice.<br class=""><br class="">Consider the barriers Swift already puts in place to prevent the bad thing (declaring an inappropriately narrow explicitly-specified throw signature) from happening:<br class=""><br class="">1) First of all, you need to declare a public API. If it isn’t public, then there is no concern at all, you can evolve the implementation and clients together.<br class=""><br class="">2) The Second problem depends on the number of errors it can throw. If there is exactly one type of error, the most common way to handle it is by returning optional. If you have one obvious failure mode with a value, then you throw that value. The most common case is where you can throw more than one sort of error, and therefore have an enum to describe it.<br class=""><br class="">3) Third, your enum needs to be declared fragile in order to allow clients to enumerate their cases specifically.<br class=""><br class="">The third step (having to mark your enum fragile, however it is spelled) is the biggest sign that you’re opting into a commitment that you should think really hard about. If folks don’t know that this is a big API commitment, then we have bigger problems.<br class=""><br class=""><br class=""><blockquote type="cite" class=""><blockquote type="cite" class="">One thing you didn’t mention is that boxing thrown values in an existential requires allocation in the general case. This may be unacceptable for some classes of Swift application (in the embedded / deep systems space) or simply undesirable because of the performance implication.<br class=""></blockquote><br class="">So, the performance implication cuts both ways. We can design the ABI for typed-throws so that, say, the callee initializes some buffer that's passed into it. That's an ABI that will kill some potential allocations in deep systems code, no question about it.<br class=""></blockquote><br class="">Agreed.<br class=""><br class=""><blockquote type="cite" class="">But in non-deep-systems code, we generally expect that error types will be resilient, which means that there are non-zero dynamic costs for allocating space on the stack for the error.<br class=""></blockquote><br class="">Proposed solution: ABI is that the callee takes in a register which is either a buffer address to fill in or null. On error, the callee returns the error pointer in a specific register. If there was a buffer passed in, it uses it, otherwise it allocates.<br class=""><br class="">In practice, this allows the compiler to only pre-allocate the buffer when it knows the fixed size, otherwise the caller allocates on the heap on demand.<br class=""><br class="">AFAICT, the cost of this API is only a “li rN, 0” in the normal path.<br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">If you really want zero mallocs, it seems to me like creative stack accounting could get you there. In cases where we concretely know we're working with an existential, we can "explode" the existential into an opened type variable and a value of that type, and avoid allocating the existential representation. In the case of an untyped or resilient error, the callee could leave the error value and its type metadata on the stack somewhere without resetting the stack pointer, stick the address of that payload somewhere, and code would then propagate up to the catcher, who's responsible for consuming the error value and popping the stack when it's done with it. We could potentially do this more generally with existential returns too, so that existentials are a viable abstraction tool even in predictable-performance mode.</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">-Joe</span></div></blockquote></div><br class=""><div class="">Surely the compiler could already do that within a single module? If it knows it’s going to get a bunch of cases from the same enum, it can have an optimised internal call. We could possibly allow exhaustive catching within the same module this way.</div><div class=""><br class=""></div><div class="">Most libraries will need the existential wrapper due to resilience concerns. Libraries that want to get this optimised, non-resilient, exhaustive error handling could opt-in somehow. I think it’s a pretty small group that really need it - perhaps they could be satisfied with throwing a single fixed enum.</div><div class=""><br class=""></div><div class="">For most practical purposes, the best thing we could do to improve our error-handling experience is have the compiler generate some minimal documentation of the public Errors which a function throws. That means you only need to document the error cases themselves, rather than documenting every function. App developers and library authors would get substantially better diagnostics and documentation with very little effort. Again, considering resilience, I think it’s a bigger win than strongly-typed throws.</div><div class=""><br class=""></div><div class="">The only language change would be that you could exhaustively catch errors within your own module. The rest is QOI.</div><div class=""><br class=""></div><div class="">- Karl</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div></body></html>