Three questions regarding this amendment:
First, regarding this rule—
If a
throws-clause
is present, then there must be at least onecatch-clause
.
Why this requirement? Could it not be useful for a non-catching do
for the purposes of closure type inference?
Second—
How do nested throws
affect type inference? The original proposal writes of func callCat() throws(CatError) -> Cat
:
The function can only throw instances of
CatError
. This provides contextual type information for all throw sites, so we can write.sleeps
instead of the more verboseCatError.sleeps
that's needed with untyped throws.
But suppose we have:
func callCat() throws(CatError) -> Cat {
do throws(DogError) {
throw .sleeps // (1)
} catch {
throw .sleeps // (2)
}
fatalError()
}
Am I correct in deducing that DogError.sleeps
is thrown at (1), which is caught as an error of static type DogError
and handled by rethrowing CatError.sleeps
at (2)?
Third—
throws-clause
not being part of this amendment (and hence not in the diff), I'm assuming this permits:
do throws /* note: no type */ { ... } catch { ... }
If so, does this "force box" the error—i.e., is it equivalent to do throws(any Error)...
—or is it always equivalent to bare do { ... } catch { ... }
?
Actually, it would seem to me that the latter wouldn't be logical, particular for nested type inference purposes. For instance, given:
// --- This all works in Swift 5.9 ---
struct E: Error { }
enum F: Error { case e }
extension E {
static var e: F { F.e } // note this twist.
}
extension Error where Self == E {
static var e: E { E() } // yes, I'm making your life difficult.
}
func e() -> Error { .e } // returns a boxed `E`
func f() -> Error { F.e } // returns a boxed `F`
func g() throws { throw .e } // throws error of type `E`
// --- As originally proposed here in SE-0413 ---
func h() throws(F) { throw .e } // throws error of type `F`
func i() throws(F) {
do { throw .e } // throws error of type `F`
}
Can you explain how the following behaves?
func j() throws {
do throws(F) {
throw .e // throws error of type `F`
} catch {
throw .e // throws error of type `E` (I think?)
}
}
// --- What happens here? ---
func k() throws(F) {
do throws(E) {
throw .e // (5)
} catch is E {
throw .e // (6)
}
// ...
}
func l() throws(F) {
do throws {
throw .e // (7)
} catch is E {
throw .e // (8)
}
// ...
}
func m() throws(F) {
do throws(any Error) {
throw .e // (9)
} catch is E {
throw .e // (10)
}
// ...
}
func n() throws(F) {
do {
throw .e // (11)
} catch is E {
throw .e // (12)
}
// ...
}