SE-0413: Typed throws

+1 for me.

@Douglas_Gregor shoudn't the stdlib rather adopt a new typed throwing Result.value property to align with other APIs like Task.value etc.? I think get() should be left as is and instead we should revive this pitch, but this time around with typed throws.

1 Like

Yes, the toolchain linked in the first post. I can get the TypedThrows feature working just fine, it's just the FullTypedThrows, that enables all of the inference rules, that I can't.

I now understand the proposal is specific because the throw CatError.asleep example is immediately source breaking if the inferred type in the catch changes, whereas the try callCats() example would require callCats() to adopt typed throws before the catch behavior changes, meaning the user is already changing their source.


I am thrilled to see this proposal - this is a bit of a "finally" moment for me. I think the integration into the existing type system is wonderful, it is great that it dovetails correctly with Never and any Error correctly, and it is super great that it defines away most of the need for rethrows (which has always been a pragmatic hack IMO, and it would be great for it to go away / turn into sugar only feature).

One question about the MapError example in this section: is that actually allowed in today's swift? I thought that rethrowing functions were not able to throw explicitly themselves, only call throwing argument closures. Is this magic behavior that is allowed in a catch block of a rethrowing function?





That was the behaviour in its very first iteration, in Swift 2. In Swift 3, rethrows was revised ("loosened") to mean what it does now: that it can throw [any type it wants] only if & when one of its arguments throws, in order to specifically enable wrapping of lower-level errors (perhaps since generic Errors are not extensible nor chainable, but it's common to want to attach additional information to an exception record as it propagates up through multiple layers of API).

For further details, see this series of archeological posts from the earlier pitch review thread.

Also, welcome back! Or are you just here to nab ideas for your new language, like @dabrahams? :grin:


Can it be? Isn't it baked into the ABI due to functions like

AFAIR, rethrows functions use the same mangling as throws. When such a function is invoked with a non-throwing closure, an unreachable "catch" branch will be emitted on the callsite.

1 Like

ABI is more than mangling, but yes---the ABI of a rethrows function is identical to that of a throws function. If you're curious, you can see what I did to swap in a typed-throws map in an ABI-compatible manner.

Thanks for trying out the toolchain! FullTypedThrows can be enabled as an "upcoming" feature (in addition to enabling TypedThrows as an experimental feature, and will change inference for the caught errors in, e.g.,

do {
  if something { throw HomeworkError.dogAteIt }
} catch {
  // error will have type HomeworkError when FullTypedThrows is enabled

However, as noted, we aren't doing type inference for closure thrown result types in the toolchain.

Right. You can actually exploit this fact to get "typed throw" behavior without FullTypedThrows being enabled, by adding this...

func typedThrow<E: Error>(_ error: E) throws(E) {
  throw error

and replacing a throw within a do..catch like this:

try typedThrow(HomeworkError.dogAteIt)

Obviously not as nice as FullTypedThrow behavior, but it's more incremental.



Yeah, I tried a bunch of permutations of FullTypedThrows as upcoming or experimental features, but it turns out the behavior I'm seeing is probably just a bug, so I filed [Typed Throws]: Error thrown from try not inferred for catch · Issue #69985 · apple/swift · GitHub. Perhaps it has difficulty with the fact the typed error comes from a protocol requirement.

1 Like

I'd expect your code to work with -enable-experimental-feature TypedThrows -enable-upcoming-feature FullTypedThrows, so this is likely a bug in the toolchain. I'll take a look, thanks!



Which part would require FullTypedThrows? From my current understanding, neither of the inference exceptions apply here. It's not a direct throw nor is it attempting to throw out of a closure.

You are right that it's not related to FullTypedThrows; it's related to the do...catch block being within a closure, which is a subset of the closure inference logic, and the toolchain isn't handling such cases yet.



Ah right, thanks for the reminder!

I don't have time to follow Swift development generally, but I'm a huge fan of it and the community of course, and typed throws is a design point I've been in favor of for a long time.

In the case of Mojo🔥, its generics system is just coming up now and yes I'm definitely interested in what Swift and other languages are doing. In this case, I'm particularly happy with how the Swift team makes it possible to abstract over the throws effect here. This has been a persistent problem in Swift for some time, and I'm interested in making sure Mojo gets this right from the beginning, and also extends it to async (our other effect).



For subtyping purposes, Never is assumed to be a subtype of all error types.

Can we please finally generalize Never as the true bottom type? This feature is a great motivation, and making it a special case feels really unfortunate.

I just want to finally be able to use fatalErrror in the nil-coalescing operator :smile: (yes I know I can use a custom operator or macro, but it would be great for it to be standard).


I think it is a good addition, but imo it is not so nice to introduce yet another meaning for parenthesis — without even mentioning an established way for type annotations (throws: E) in the alternatives section.

If you think of it as taking an additional parameter, it has the same meaning as most of the other paren usage in the language.


Under the hood functions with untyped throws actually have an error parameter: a pointer to the existential box to put an error value into if it were thrown.

pass a pointer argument that doesn't interfere with the normal argument sequence. The caller would initialize the memory to a zero value. If the callee is a throwing function, it would be expected to write the error value into this argument

1 Like

This is getting off-topic, but it's not really a pointer to the error box. With optimal calling convention support, it's more like we return a pointer to the box in an extra return-value register. Without that support, we pass a pointer to a variable into which to write the pointer to the box.

The typed throws calling convention, in the pessimal case, degrades to something more like what you're saying, except that there's no box: the caller passes a pointer into which the callee should write the error (plus some other mechanics for determining whether an error was thrown, some of which haven't yet been done the way we want).


What is your evaluation of the proposal?

I love it! Big +1 from me. This was the feature I was missing most in Swift. And the way it is executed makes a lot of sense and fits right into the existing feature set and APIs.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes, definitely! It will entirely change the way I do error handling in my apps, mostly making it easier and clearer. This feature will directly correlate with how useful error messages will be to my users. Also, some well-chosen functions in my open-source libraries might use this, too.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

No in-depth study of the implementation, but I have thought about error handling in Swift in depth. I even prepared an error-handling framework for app developers based on the Result type to work around the current lack of typed throws in the language (I didn't release it yet because I saw this proposal coming which will have a big impact on that framework). I've read the full proposal twice (first an earlier version of it) – note though that I'm no compiler expert and do not understand the Swift grammar part fully.

Does this proposal fit well with the feel and direction of Swift?

The Swift website states Swift is "modern, safe, and a joy to write". This proposal makes Swift even more modern, safe, and a joy to write. So: Yes! Less guessing of potential error types for APIs where they can be made clear. This means basically all of my own error types I create within my apps can now be clearly communicated both to the compiler and to the users of those APIs. This will reduce a lot of boilerplate I currently have in my code and make my code safer while reducing cognitive load by offloading that to the compiler, improving safety & joy. (I had already stated why untyped throws are not safe here.) And the way it still supports the untyped throws feels very modern, giving the developers the choice. Another useful tool in their belt.