Wow. So. I decided I wanted to poke around with Swift, stumbled into the Using Swift section, and fell right into this because the title looked interesting. Glad I did, this was pretty neat! This turned into an itch I had to scratch, so I figured I’d take a swing with what I managed to figure out.
The docs kind of bury the reason here, but it seems to come down to the function’s type. async and throws are part of the function’s type signature, right? So then there are rules around what function type’s can override or overload other functions based on these type signatures.
If we look at the declaration docs for throws, it calls out the rules for overloading and overriding throws functions. Throwing methods can not override non-throwing methods, but the inverse is fine which we see in your first test. Non throwing methods are a sub-type of throwing, which is fine and so it works!
We also see in your second test another point mentioned in the docs, that marking a function as throws is not sufficient to overload, but marking a param to the function throws is. So only removing throws does not sufficiently change the function type to trigger an overload. Cool, this all seems to make sense to me.
Now if we look at the declaration docs for async, it contains a similar section. In this case you can overload a function based only on async. Well, that explains your second test, it’s overloaded and allowed. The compiler should choose the proper overload based on call-site context, so we’re all good here. (Note: There are some bugs around this behavior where the compiler does not “do the right thing”, but that seems to be a different issue.).
So, if we look at the override section, it says async can’t override sync, but sync can override async, right? Well, uh.. apparently not? That’s the whole issue with your first test isn’t it? So what gives?
Here is where it starts going sideways according to my understanding.. The compiler tests added with the implementation of the async/await proposal appear to have always indicated that overriding an async method with a sync method should fail for class inheritance, which seems to go counter to the docs. Further still, the same restriction did originally apply to protocols, but were changed and do allow you to override an async method with a sync method.
So I guess I’m sitting right there with you, feeling no smarter in the end, and wondering if this is a bug or a docs error.. If anyone has insight on what I missed or can help explain the specific behavior with classes, that’d be great. All I could find was in the original proposal where it again mentions async being part of the function type, but that still doesn’t explain the quirk?