diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index a24b04e264314..010430fb818aa 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5461,6 +5461,10 @@ 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_throw_in_objc_forbidden,none, + "typed 'throws' %kindonly0 cannot be represented in Objective-C", + (const AbstractFunctionDecl *)) + //------------------------------------------------------------------------------ // MARK: Concurrency //------------------------------------------------------------------------------ diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 1a1170b0bed32..ed6144a8f0261 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -810,6 +810,23 @@ bool swift::isRepresentableInLanguage( } } + // Check that @objc functions can't have typed throw. + if (AFD->hasThrows()) { + Type thrownType = AFD->getThrownInterfaceType(); + // TODO: only `throws(Error)` is allowed. + // Throwing `any MyError` that confronts `Error` is not implemented yet. + // Shall we allow `any MyError` in the future, we should check against + // `isExistentialType` instead, and other type checks will make sure it + // confrons to `Error`. + if (thrownType && !thrownType->isErrorExistentialType()) { + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::typed_throw_in_objc_forbidden, AFD) + .limitBehavior(behavior)); + Reason.describe(AFD); + return false; + } + } + if (AFD->hasAsync()) { // Asynchronous functions move all of the result value and thrown error // information into a completion handler. diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index 9f48e3dfbda6a..13b99bfadcf71 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -2268,6 +2268,11 @@ extension Protocol_ObjC1 { //===--- //===--- Error handling //===--- +enum ObjCError: Int, Error { + case Others +} +protocol MyError: Error { } + // CHECK-LABEL: class ClassThrows1 class ClassThrows1 { // CHECK: @objc func methodReturnsVoid() throws @@ -2329,6 +2334,15 @@ class ClassThrows1 { // CHECK: {{^}} func fooWithErrorProtocolComposition2(x: any Error & Protocol_ObjC1) func fooWithErrorProtocolComposition2(x: Error & Protocol_ObjC1) { } + + @objc func throwsError() throws(Error) {} + @objc func throwsMyError() throws(MyError) {} + // expected-error@-1{{typed 'throws' instance method cannot be represented in Objective-C}} + // expected-error@-2{{thrown type 'any MyError' does not conform to the 'Error' protocol}} + @objc func throwsObjCError() throws(ObjCError) {} + // expected-error@-1{{typed 'throws' instance method cannot be represented in Objective-C}} + @objc static func throwsObjCErrorClass() throws(ObjCError) {} + // expected-error@-1{{typed 'throws' static method cannot be represented in Objective-C}} } diff --git a/test/attr/attr_objc_async.swift b/test/attr/attr_objc_async.swift index e42f9d343ec15..107bba5f9094b 100644 --- a/test/attr/attr_objc_async.swift +++ b/test/attr/attr_objc_async.swift @@ -27,6 +27,10 @@ class MyClass { // actor exporting Objective-C entry points. +enum ObjCError: Int, Error { + case Others +} + // CHECK: actor MyActor actor MyActor { // CHECK: @objc func doBigJobActor() async -> Int @@ -49,6 +53,10 @@ actor MyActor { // CHECK: @objc nonisolated func synchronousGood() @objc nonisolated func synchronousGood() { } + + @objc func objcAsyncThrowsError() async throws(Error) -> Void {} + @objc func objcAsyncThrowsObjCError() async throws(ObjCError) -> Void {} + // expected-error@-1 {{typed 'throws' instance method cannot be represented in Objective-C}} } actor class MyActor2 { }