Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
//------------------------------------------------------------------------------
Expand Down
17 changes: 17 additions & 0 deletions lib/Sema/TypeCheckDeclObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
14 changes: 14 additions & 0 deletions test/attr/attr_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}}
}


Expand Down
8 changes: 8 additions & 0 deletions test/attr/attr_objc_async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 { }
Expand Down