-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Sema] @objc functions shall not have typed throw #81054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Peter Rong <[email protected]>
@swift-ci please test |
@swift-ci Please Test Source Compatibility Release |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @DataCorrupted. You've got the spirit right, but let's refine some of the details.
lib/Sema/TypeCheckDecl.cpp
Outdated
if (AFD->getAttrs().hasAttribute<ObjCAttr>()) { | ||
Context.Diags.diagnose(loc, diag::typed_thrown_in_objc_forbidden); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably don't want to check this in InterfaceTypeRequest::evaluate()
—@objc
can be inferred in many situations, and that inference won't take your new rule into account if you add it here.
To make sure this works with @objc
inference, modify swift::isRepresentableInLanguage()
(in TypeCheckDeclObjC.cpp) or one of its callees to make it say that a typed-throws method is not representable as an @objc
declaration. This function should return false
(and, depending on the ObjCReason
it's passed, likely diagnose an error) if the function it's passed isn't allowed to be @objc
.
For this specific task, look at the places where that function checks hasThrows()
to find the places where you'll need to add logic. There will be two places you need to modify—one for just throws
, another for async throws
—so make sure you add this logic for both of them, and make sure you have test cases for both of them. (There will also be a couple of places where you don't need to change anything, though, so read the code around each call to figure out what's going on there.)
Also, take a look at how other diagnostics in this function use limitBehavior()
and softenIfAccessNote()
and try to do the same for your diagnostic. It's a little funky, so if you can't figure it out, feel free to ask for help!
test/decl/func/typed_throws.swift
Outdated
@objc class ObjCClass: NSObject { | ||
@objc func objcTypedThrow() throws(ObjCError) -> () {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of Swift's platforms don't support @objc
, so if you're going to test something with @objc
, you'll need to do it in a test file that only runs if ObjC interop is supported.
For this change, I'll recommend adding your tests to test/attr/attr_objc.swift. There are already some tests of ordinary throws (search for ClassThrows1
), so you should be able to add some typed throws tests somewhere around there.
(As a nice bonus, this file already has infrastructure in place to check that you've used softenIfAccessNote()
correctly.)
Like I mentioned before, you should also test typed throws with async
; test/attr/attr_objc_async.swift looks like a good place to do that.
Signed-off-by: Peter Rong <[email protected]>
Signed-off-by: Peter Rong <[email protected]>
@swift-ci please test |
1 similar comment
@swift-ci please test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! The tests are perfect—just a few more little refinements to the implementation...
lib/Sema/TypeCheckDeclObjC.cpp
Outdated
if (!AFD->getThrownTypeRepr()) | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a subtle pitfall here: A TypeRepr
is an exact representation of how a type is written in source code, so it's only available when the Swift compiler has directly parsed source code to create the declaration. If a declaration uses typed throws but it was deserialized from a .swiftmodule
file or synthesized by the compiler, then getThrownTypeRepr()
will return nullptr
even though there is actually a typed throws.
The various methods that return the thrown type as a Type
, rather than a TypeRepr *
, don't have this problem, so you can just remove the getThrownTypeRepr()
check—checking getThrownInterfaceType()
alone will tell you everything you need to know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am fairly new to swift, can you elaborate more on "If a declaration uses typed throws but it was deserialized from a .swiftmodule file or synthesized by the compiler", how to create either case, and how are they used in practice?
lib/Sema/TypeCheckDeclObjC.cpp
Outdated
// Throwing `any MyError` that confronts `Error` is not implemented yet. | ||
// Shall we allow `any MyError` in the future, we should check against | ||
// `isExistentialType` instead. | ||
if (thrownType->isErrorExistentialType()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Digging into the implementation underlying getThrownInterfaceType()
, it turns out that if a function is non-throwing, it will return the type for Swift.Never
. So if you wrote this condition:
if (thrownType->isErrorExistentialType()) | |
if (thrownType->isNever() || thrownType->isErrorExistentialType()) |
You would not need to explicitly check hasThrows()
before calling your isTypedThrow()
function.
(If you don't do the isNever()
check, you might want to remove the call to getCanonicalType()
on line 817—TypeBase::isErrorExistentialType()
implicitly canonicalizes the type, so you don't have to.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thrownType
could be a null pointer if not getCanonicalType
, even if AFD hasThrow
. I'm not sure why.
I'd prefer keep the hasThrow
check to make the intention more clear, it also made more sense to erase the closure that this point.
lib/Sema/TypeCheckDeclObjC.cpp
Outdated
Reason.describe(AFD); | ||
return true; | ||
}; | ||
if (AFD->hasThrows() && isTypedThrow()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, checking this before the sync and async code paths diverge is clever!
@@ -5461,6 +5461,9 @@ WARNING(no_throw_in_do_with_catch,none, | |||
ERROR(thrown_type_not_error,none, | |||
"thrown type %0 does not conform to the 'Error' protocol", (Type)) | |||
|
|||
ERROR(typed_thrown_in_objc_forbidden,none, | |||
"@objc functions cannot have typed throw", ()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably word this more similarly to the other diagnostics emitted by isRepresentableInLanguage()
, such as not_objc_function_async
:
"@objc functions cannot have typed throw", ()) | |
"typed 'throws' %kindonly0 cannot be represented in Objective-C", | |
(const AbstractFunctionDecl *)) |
Note that this will insert a string like "instance method" where %kindonly0
is written—you'll need to pass the decl into the diagnose()
call, though.
(There are other ways you might word this—if you'd like, take a look around at a few other diagnostics in this method and see if there's something you like better.)
Signed-off-by: Peter Rong <[email protected]>
@swift-ci Please test |
@beccadax Hi Becca, do you think this PR is ready to land? Let me know if you wish me to change anything else. |
Amends SE-0413 and resolves #80974
cc @drodriguez @AdamCmiel @DougGregor