From b664a403a5447fe67c854eaef58593b7b8f1a8e3 Mon Sep 17 00:00:00 2001 From: Peter Rong Date: Wed, 23 Apr 2025 14:19:37 -0700 Subject: [PATCH 1/8] [Sema] Type checking forbids typed throw on @objc functions now Signed-off-by: Peter Rong --- include/swift/AST/DiagnosticsSema.def | 3 +++ lib/Sema/TypeCheckDecl.cpp | 13 +++++++------ test/decl/func/typed_throws.swift | 6 ++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index a24b04e264314..9929f85d455b4 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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, + "typed throw can't be applied to @objc functions", ()) + //------------------------------------------------------------------------------ // MARK: Concurrency //------------------------------------------------------------------------------ diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 0089ccc9da1a1..4b489b8315e6b 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2569,12 +2569,13 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { ProtocolDecl *errorProto = Context.getErrorDecl(); if (thrownTy && !thrownTy->hasError() && errorProto) { Type thrownTyInContext = AFD->mapTypeIntoContext(thrownTy); - if (!checkConformance(thrownTyInContext, errorProto)) { - SourceLoc loc; - if (auto thrownTypeRepr = AFD->getThrownTypeRepr()) - loc = thrownTypeRepr->getLoc(); - else - loc = AFD->getLoc(); + auto thrownTypeRepr = AFD->getThrownTypeRepr(); + SourceLoc loc = + (thrownTypeRepr) ? thrownTypeRepr->getLoc() : AFD->getLoc(); + if (AFD->getAttrs().hasAttribute()) { + Context.Diags.diagnose(loc, diag::typed_thrown_in_objc_forbidden, + thrownTy); + } else if (!checkConformance(thrownTyInContext, errorProto)) { Context.Diags.diagnose(loc, diag::thrown_type_not_error, thrownTy); } } diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index c453ed0d27f61..d975aa034aa25 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -216,3 +216,9 @@ struct NotAnError {} func badThrowingFunctionType(_: () throws(NotAnError) -> ()) {} // expected-error@-1 {{thrown type 'NotAnError' does not conform to the 'Error' protocol}} + +enum ObjCError: Int, Error { + case Others +} +@objc func objcTypedThrow() throws(ObjCError) -> () {} +// expected-error@-1 {{typed throw can't be applied to @objc functions}} From cf15d4613f8b5190080272b3639fec850873b98a Mon Sep 17 00:00:00 2001 From: Peter Rong Date: Wed, 23 Apr 2025 15:17:41 -0700 Subject: [PATCH 2/8] Update --- include/swift/AST/DiagnosticsSema.def | 36 +++++++++++++-------------- lib/Sema/TypeCheckDecl.cpp | 5 ++-- test/decl/func/typed_throws.swift | 2 +- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 9929f85d455b4..510f5d7d4bb1d 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -114,7 +114,7 @@ ERROR(could_not_find_imported_enum_case,none, NOTE(did_you_mean_raw_type,none, "did you mean to specify a raw type on the enum declaration?", ()) - + NOTE(did_you_mean_generic_param_as_conformance,none, "did you mean to declare %0 as a protocol conformance for %1?", (DeclName, Type)) @@ -1273,7 +1273,7 @@ REMARK(transitive_dependency_behavior,none, "%1 has %select{a required|an optional|an ignored}2 " "transitive dependency on '%0'", (StringRef, Identifier, unsigned)) - + REMARK(explicit_interface_build_skipped,none, "Skipped rebuilding module at %0 - up-to-date", (StringRef)) @@ -1527,7 +1527,7 @@ ERROR(optional_self_not_unwrapped,none, "to make control flow explicit", ()) NOTE(optional_self_chain,none, "reference 'self?.' explicitly", ()) - + ERROR(missing_unwrap_optional_try,none, "value of optional type %0 not unwrapped; did you mean to use 'try!' " "or chain with '?'?", @@ -1538,7 +1538,7 @@ NOTE(missing_forced_downcast, none, "did you mean to use 'as!' to force downcast?", ()) NOTE(missing_optional_downcast, none, "did you mean to use 'as?' to conditionally downcast?", ()) - + WARNING(coercion_may_fail_warning,none, "coercion from %0 to %1 may fail; use 'as?' or 'as!' instead", (Type, Type)) @@ -3984,7 +3984,7 @@ NOTE(automatic_protocol_synthesis_unsupported,none, "automatic synthesis of %0 is not supported for %kindonly1 declarations", (const ProtocolDecl *, const NominalTypeDecl *)) NOTE(comparable_synthesis_raw_value_not_allowed, none, - "enum declares raw type %0, preventing synthesized conformance of %1 to %2", + "enum declares raw type %0, preventing synthesized conformance of %1 to %2", (Type, Type, Type)) // Dynamic Self @@ -4232,7 +4232,7 @@ ERROR(attr_rawlayout_cannot_have_alignment_attr,none, "type with @_rawLayout cannot also have an @_alignment attribute", ()) ERROR(attr_rawlayout_invalid_count_type,none, "@_rawLayout count must either be integer literal or a generic argument", ()) - + // lazy ERROR(lazy_not_on_let,none, "'lazy' cannot be used on a let", ()) @@ -4576,15 +4576,15 @@ ERROR(unordered_adjacent_operators,none, ERROR(missing_builtin_precedence_group,none, "broken standard library: missing builtin precedence group %0", (Identifier)) -WARNING(nan_comparison, none, +WARNING(nan_comparison, none, "comparison with '.nan' using %0 is always %select{false|true}1, use " "'%2.isNaN' to check if '%3' %select{is not a number|is a number}1", (Identifier, bool, StringRef, StringRef)) -WARNING(nan_comparison_without_isnan, none, - "comparison with '.nan' using %0 is always %select{false|true}1", +WARNING(nan_comparison_without_isnan, none, + "comparison with '.nan' using %0 is always %select{false|true}1", (Identifier, bool)) -WARNING(nan_comparison_both_nan, none, - "'.nan' %0 '.nan' is always %select{false|true}1", +WARNING(nan_comparison_both_nan, none, + "'.nan' %0 '.nan' is always %select{false|true}1", (StringRef, bool)) // If you change this, also change enum TryKindForDiagnostics. @@ -4672,7 +4672,7 @@ ERROR(could_not_infer_pack_element,none, NOTE(note_in_opening_pack_element,none, "in inferring pack element #%0 of '%1'", (unsigned,StringRef)) - + ERROR(type_of_expression_is_ambiguous,none, "type of expression is ambiguous without a type annotation", ()) @@ -4728,7 +4728,7 @@ ERROR(should_use_empty_dictionary_literal,none, WARNING(duplicated_literal_keys_in_dictionary_literal, none, "dictionary literal of type %0 has duplicate entries for %1 literal key%select{ %3|}2", (Type, StringRef, bool, StringRef)) -NOTE(duplicated_key_declared_here, none, +NOTE(duplicated_key_declared_here, none, "duplicate key declared here", ()) // Generic specializations @@ -5462,7 +5462,7 @@ ERROR(thrown_type_not_error,none, "thrown type %0 does not conform to the 'Error' protocol", (Type)) ERROR(typed_thrown_in_objc_forbidden,none, - "typed throw can't be applied to @objc functions", ()) + "@objc functions cannot have typed throw", ()) //------------------------------------------------------------------------------ // MARK: Concurrency @@ -5603,7 +5603,7 @@ NOTE(property_requires_actor,none, "initializer for %kind0 is %1", (const VarDecl *, ActorIsolation)) ERROR(isolated_default_argument_context,none, - "%0 default value in a %1 context", + "%0 default value in a %1 context", (ActorIsolation, ActorIsolation)) ERROR(conflicting_default_argument_isolation,none, "default argument cannot be both %0 and %1", @@ -6359,7 +6359,7 @@ ERROR(differentiable_function_type_void_result, "differentiable inout parameter, i.e. a non-'@noDerivative' parameter " "whose type conforms to 'Differentiable'", ()) -ERROR(differentiable_function_type_no_differentiability_parameters, +ERROR(differentiable_function_type_no_differentiability_parameters, none, "'@differentiable' function type requires at least one differentiability " "parameter, i.e. a non-'@noDerivative' parameter whose type conforms to " @@ -8357,7 +8357,7 @@ ERROR(safe_and_unsafe_attr,none, "%kindbase0 cannot be both @safe and @unsafe", (const Decl *)) GROUPED_WARNING(unsafe_superclass,StrictMemorySafety,none, - "%kindbase0 has superclass involving unsafe type %1", + "%kindbase0 has superclass involving unsafe type %1", (const ValueDecl *, Type)) GROUPED_WARNING(conformance_involves_unsafe,StrictMemorySafety,none, @@ -8674,7 +8674,7 @@ GROUPED_WARNING( ERROR(extensible_attr_on_frozen_type,none, "cannot use '@extensible' together with '@frozen'", ()) -ERROR(extensible_attr_on_internal_type,none, +ERROR(extensible_attr_on_internal_type,none, "'@extensible' attribute can only be applied to public or package " "declarations, but %0 is " "%select{private|fileprivate|internal|%error|%error|%error}1", diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 4b489b8315e6b..a76dfbed2b42a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2572,9 +2572,8 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { auto thrownTypeRepr = AFD->getThrownTypeRepr(); SourceLoc loc = (thrownTypeRepr) ? thrownTypeRepr->getLoc() : AFD->getLoc(); - if (AFD->getAttrs().hasAttribute()) { - Context.Diags.diagnose(loc, diag::typed_thrown_in_objc_forbidden, - thrownTy); + if (AFD->getAttrs().hasAttribute() && thrownTy) { + Context.Diags.diagnose(loc, diag::typed_thrown_in_objc_forbidden); } else if (!checkConformance(thrownTyInContext, errorProto)) { Context.Diags.diagnose(loc, diag::thrown_type_not_error, thrownTy); } diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index d975aa034aa25..8e0a5dc91dfff 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -221,4 +221,4 @@ enum ObjCError: Int, Error { case Others } @objc func objcTypedThrow() throws(ObjCError) -> () {} -// expected-error@-1 {{typed throw can't be applied to @objc functions}} +// expected-error@-1 {{@objc functions cannot have typed throw}} From 813aae6cc6a00c284c5254b099da4860af46c5e3 Mon Sep 17 00:00:00 2001 From: Peter Rong Date: Wed, 23 Apr 2025 15:18:34 -0700 Subject: [PATCH 3/8] Formating --- include/swift/AST/DiagnosticsSema.def | 34 +++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 510f5d7d4bb1d..6fcb86b93fc48 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -114,7 +114,7 @@ ERROR(could_not_find_imported_enum_case,none, NOTE(did_you_mean_raw_type,none, "did you mean to specify a raw type on the enum declaration?", ()) - + NOTE(did_you_mean_generic_param_as_conformance,none, "did you mean to declare %0 as a protocol conformance for %1?", (DeclName, Type)) @@ -1273,7 +1273,7 @@ REMARK(transitive_dependency_behavior,none, "%1 has %select{a required|an optional|an ignored}2 " "transitive dependency on '%0'", (StringRef, Identifier, unsigned)) - + REMARK(explicit_interface_build_skipped,none, "Skipped rebuilding module at %0 - up-to-date", (StringRef)) @@ -1527,7 +1527,7 @@ ERROR(optional_self_not_unwrapped,none, "to make control flow explicit", ()) NOTE(optional_self_chain,none, "reference 'self?.' explicitly", ()) - + ERROR(missing_unwrap_optional_try,none, "value of optional type %0 not unwrapped; did you mean to use 'try!' " "or chain with '?'?", @@ -1538,7 +1538,7 @@ NOTE(missing_forced_downcast, none, "did you mean to use 'as!' to force downcast?", ()) NOTE(missing_optional_downcast, none, "did you mean to use 'as?' to conditionally downcast?", ()) - + WARNING(coercion_may_fail_warning,none, "coercion from %0 to %1 may fail; use 'as?' or 'as!' instead", (Type, Type)) @@ -3984,7 +3984,7 @@ NOTE(automatic_protocol_synthesis_unsupported,none, "automatic synthesis of %0 is not supported for %kindonly1 declarations", (const ProtocolDecl *, const NominalTypeDecl *)) NOTE(comparable_synthesis_raw_value_not_allowed, none, - "enum declares raw type %0, preventing synthesized conformance of %1 to %2", + "enum declares raw type %0, preventing synthesized conformance of %1 to %2", (Type, Type, Type)) // Dynamic Self @@ -4232,7 +4232,7 @@ ERROR(attr_rawlayout_cannot_have_alignment_attr,none, "type with @_rawLayout cannot also have an @_alignment attribute", ()) ERROR(attr_rawlayout_invalid_count_type,none, "@_rawLayout count must either be integer literal or a generic argument", ()) - + // lazy ERROR(lazy_not_on_let,none, "'lazy' cannot be used on a let", ()) @@ -4576,15 +4576,15 @@ ERROR(unordered_adjacent_operators,none, ERROR(missing_builtin_precedence_group,none, "broken standard library: missing builtin precedence group %0", (Identifier)) -WARNING(nan_comparison, none, +WARNING(nan_comparison, none, "comparison with '.nan' using %0 is always %select{false|true}1, use " "'%2.isNaN' to check if '%3' %select{is not a number|is a number}1", (Identifier, bool, StringRef, StringRef)) -WARNING(nan_comparison_without_isnan, none, - "comparison with '.nan' using %0 is always %select{false|true}1", +WARNING(nan_comparison_without_isnan, none, + "comparison with '.nan' using %0 is always %select{false|true}1", (Identifier, bool)) -WARNING(nan_comparison_both_nan, none, - "'.nan' %0 '.nan' is always %select{false|true}1", +WARNING(nan_comparison_both_nan, none, + "'.nan' %0 '.nan' is always %select{false|true}1", (StringRef, bool)) // If you change this, also change enum TryKindForDiagnostics. @@ -4672,7 +4672,7 @@ ERROR(could_not_infer_pack_element,none, NOTE(note_in_opening_pack_element,none, "in inferring pack element #%0 of '%1'", (unsigned,StringRef)) - + ERROR(type_of_expression_is_ambiguous,none, "type of expression is ambiguous without a type annotation", ()) @@ -4728,7 +4728,7 @@ ERROR(should_use_empty_dictionary_literal,none, WARNING(duplicated_literal_keys_in_dictionary_literal, none, "dictionary literal of type %0 has duplicate entries for %1 literal key%select{ %3|}2", (Type, StringRef, bool, StringRef)) -NOTE(duplicated_key_declared_here, none, +NOTE(duplicated_key_declared_here, none, "duplicate key declared here", ()) // Generic specializations @@ -5603,7 +5603,7 @@ NOTE(property_requires_actor,none, "initializer for %kind0 is %1", (const VarDecl *, ActorIsolation)) ERROR(isolated_default_argument_context,none, - "%0 default value in a %1 context", + "%0 default value in a %1 context", (ActorIsolation, ActorIsolation)) ERROR(conflicting_default_argument_isolation,none, "default argument cannot be both %0 and %1", @@ -6359,7 +6359,7 @@ ERROR(differentiable_function_type_void_result, "differentiable inout parameter, i.e. a non-'@noDerivative' parameter " "whose type conforms to 'Differentiable'", ()) -ERROR(differentiable_function_type_no_differentiability_parameters, +ERROR(differentiable_function_type_no_differentiability_parameters, none, "'@differentiable' function type requires at least one differentiability " "parameter, i.e. a non-'@noDerivative' parameter whose type conforms to " @@ -8357,7 +8357,7 @@ ERROR(safe_and_unsafe_attr,none, "%kindbase0 cannot be both @safe and @unsafe", (const Decl *)) GROUPED_WARNING(unsafe_superclass,StrictMemorySafety,none, - "%kindbase0 has superclass involving unsafe type %1", + "%kindbase0 has superclass involving unsafe type %1", (const ValueDecl *, Type)) GROUPED_WARNING(conformance_involves_unsafe,StrictMemorySafety,none, @@ -8674,7 +8674,7 @@ GROUPED_WARNING( ERROR(extensible_attr_on_frozen_type,none, "cannot use '@extensible' together with '@frozen'", ()) -ERROR(extensible_attr_on_internal_type,none, +ERROR(extensible_attr_on_internal_type,none, "'@extensible' attribute can only be applied to public or package " "declarations, but %0 is " "%select{private|fileprivate|internal|%error|%error|%error}1", From 3afddfa7ec26b5200ddfdf0f91b40415519efb09 Mon Sep 17 00:00:00 2001 From: Peter Rong Date: Wed, 23 Apr 2025 15:19:17 -0700 Subject: [PATCH 4/8] Check for @objc only --- lib/Sema/TypeCheckDecl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index a76dfbed2b42a..05d64a11c4ad8 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2572,7 +2572,7 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { auto thrownTypeRepr = AFD->getThrownTypeRepr(); SourceLoc loc = (thrownTypeRepr) ? thrownTypeRepr->getLoc() : AFD->getLoc(); - if (AFD->getAttrs().hasAttribute() && thrownTy) { + if (AFD->getAttrs().hasAttribute()) { Context.Diags.diagnose(loc, diag::typed_thrown_in_objc_forbidden); } else if (!checkConformance(thrownTyInContext, errorProto)) { Context.Diags.diagnose(loc, diag::thrown_type_not_error, thrownTy); From 841b5834aea6b738c700712eee656d1aa75a8c65 Mon Sep 17 00:00:00 2001 From: Peter Rong Date: Wed, 23 Apr 2025 15:53:24 -0700 Subject: [PATCH 5/8] Add Foundation --- test/decl/func/typed_throws.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index 8e0a5dc91dfff..fa85f65b9d363 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -220,5 +220,9 @@ func badThrowingFunctionType(_: () throws(NotAnError) -> ()) {} enum ObjCError: Int, Error { case Others } -@objc func objcTypedThrow() throws(ObjCError) -> () {} -// expected-error@-1 {{@objc functions cannot have typed throw}} + +import Foundation +@objc class ObjCClass: NSObject { + @objc func objcTypedThrow() throws(ObjCError) -> () {} + // expected-error@-1 {{@objc functions cannot have typed throw}} +} From ea05881ebb3323a27e85d0969492dc86811192c8 Mon Sep 17 00:00:00 2001 From: Peter Rong Date: Thu, 24 Apr 2025 16:45:08 -0700 Subject: [PATCH 6/8] Address comments Signed-off-by: Peter Rong --- lib/Sema/TypeCheckDecl.cpp | 12 ++++++------ lib/Sema/TypeCheckDeclObjC.cpp | 23 +++++++++++++++++++++++ test/attr/attr_objc.swift | 12 ++++++++++++ test/attr/attr_objc_async.swift | 8 ++++++++ test/decl/func/typed_throws.swift | 10 ---------- 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 05d64a11c4ad8..0089ccc9da1a1 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2569,12 +2569,12 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { ProtocolDecl *errorProto = Context.getErrorDecl(); if (thrownTy && !thrownTy->hasError() && errorProto) { Type thrownTyInContext = AFD->mapTypeIntoContext(thrownTy); - auto thrownTypeRepr = AFD->getThrownTypeRepr(); - SourceLoc loc = - (thrownTypeRepr) ? thrownTypeRepr->getLoc() : AFD->getLoc(); - if (AFD->getAttrs().hasAttribute()) { - Context.Diags.diagnose(loc, diag::typed_thrown_in_objc_forbidden); - } else if (!checkConformance(thrownTyInContext, errorProto)) { + if (!checkConformance(thrownTyInContext, errorProto)) { + SourceLoc loc; + if (auto thrownTypeRepr = AFD->getThrownTypeRepr()) + loc = thrownTypeRepr->getLoc(); + else + loc = AFD->getLoc(); Context.Diags.diagnose(loc, diag::thrown_type_not_error, thrownTy); } } diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 1a1170b0bed32..60aba18740217 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -809,6 +809,24 @@ bool swift::isRepresentableInLanguage( return false; } } + auto isTypedThrow = [&]() { + // With no AST token this should be `throws` + if (!AFD->getThrownTypeRepr()) + return false; + // Or it is a `throws()` + CanType thrownType = AFD->getThrownInterfaceType()->getCanonicalType(); + // 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. + if (thrownType->isErrorExistentialType()) + return false; + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::typed_thrown_in_objc_forbidden) + .limitBehavior(behavior)); + Reason.describe(AFD); + return true; + }; if (AFD->hasAsync()) { // Asynchronous functions move all of the result value and thrown error @@ -888,6 +906,8 @@ bool swift::isRepresentableInLanguage( // a thrown error. std::optional completionHandlerErrorParamIndex; if (FD->hasThrows()) { + if (isTypedThrow()) + return false; completionHandlerErrorParamIndex = completionHandlerParams.size(); auto errorType = ctx.getErrorExistentialType(); addCompletionHandlerParam(OptionalType::get(errorType)); @@ -912,6 +932,9 @@ bool swift::isRepresentableInLanguage( SourceLoc throwsLoc; Type resultType; + if (isTypedThrow()) + return false; + const ConstructorDecl *ctor = nullptr; if (auto func = dyn_cast(AFD)) { resultType = func->getResultInterfaceType(); diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index 9f48e3dfbda6a..10844d379b382 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,13 @@ 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{{@objc functions cannot have typed throw}} + // expected-error@-2{{thrown type 'any MyError' does not conform to the 'Error' protocol}} + @objc func throwsObjCError() throws(ObjCError) {} + // expected-error@-1{{@objc functions cannot have typed throw}} } diff --git a/test/attr/attr_objc_async.swift b/test/attr/attr_objc_async.swift index e42f9d343ec15..fb3f97de91061 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 {{@objc functions cannot have typed throw}} } actor class MyActor2 { } diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index fa85f65b9d363..c453ed0d27f61 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -216,13 +216,3 @@ struct NotAnError {} func badThrowingFunctionType(_: () throws(NotAnError) -> ()) {} // expected-error@-1 {{thrown type 'NotAnError' does not conform to the 'Error' protocol}} - -enum ObjCError: Int, Error { - case Others -} - -import Foundation -@objc class ObjCClass: NSObject { - @objc func objcTypedThrow() throws(ObjCError) -> () {} - // expected-error@-1 {{@objc functions cannot have typed throw}} -} From 05644c17eed639b9b22850c6bbc87fdb01f379d5 Mon Sep 17 00:00:00 2001 From: Peter Rong Date: Thu, 24 Apr 2025 16:58:24 -0700 Subject: [PATCH 7/8] Check typed thrown early Signed-off-by: Peter Rong --- lib/Sema/TypeCheckDeclObjC.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 60aba18740217..8766967cdf3d6 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -827,6 +827,8 @@ bool swift::isRepresentableInLanguage( Reason.describe(AFD); return true; }; + if (AFD->hasThrows() && isTypedThrow()) + return false; if (AFD->hasAsync()) { // Asynchronous functions move all of the result value and thrown error @@ -906,8 +908,6 @@ bool swift::isRepresentableInLanguage( // a thrown error. std::optional completionHandlerErrorParamIndex; if (FD->hasThrows()) { - if (isTypedThrow()) - return false; completionHandlerErrorParamIndex = completionHandlerParams.size(); auto errorType = ctx.getErrorExistentialType(); addCompletionHandlerParam(OptionalType::get(errorType)); @@ -932,9 +932,6 @@ bool swift::isRepresentableInLanguage( SourceLoc throwsLoc; Type resultType; - if (isTypedThrow()) - return false; - const ConstructorDecl *ctor = nullptr; if (auto func = dyn_cast(AFD)) { resultType = func->getResultInterfaceType(); From 5af6e5e129613a003a65a9723f5c6487370de56c Mon Sep 17 00:00:00 2001 From: Peter Rong Date: Fri, 25 Apr 2025 23:24:20 -0700 Subject: [PATCH 8/8] Address comments Signed-off-by: Peter Rong --- include/swift/AST/DiagnosticsSema.def | 5 +++-- lib/Sema/TypeCheckDeclObjC.cpp | 29 ++++++++++++--------------- test/attr/attr_objc.swift | 6 ++++-- test/attr/attr_objc_async.swift | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 6fcb86b93fc48..010430fb818aa 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5461,8 +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", ()) +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 8766967cdf3d6..ed6144a8f0261 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -809,26 +809,23 @@ bool swift::isRepresentableInLanguage( return false; } } - auto isTypedThrow = [&]() { - // With no AST token this should be `throws` - if (!AFD->getThrownTypeRepr()) - return false; - // Or it is a `throws()` - CanType thrownType = AFD->getThrownInterfaceType()->getCanonicalType(); + + // 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. - if (thrownType->isErrorExistentialType()) + // `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; - softenIfAccessNote(AFD, Reason.getAttr(), - AFD->diagnose(diag::typed_thrown_in_objc_forbidden) - .limitBehavior(behavior)); - Reason.describe(AFD); - return true; - }; - if (AFD->hasThrows() && isTypedThrow()) - return false; + } + } if (AFD->hasAsync()) { // Asynchronous functions move all of the result value and thrown error diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index 10844d379b382..13b99bfadcf71 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -2337,10 +2337,12 @@ class ClassThrows1 { @objc func throwsError() throws(Error) {} @objc func throwsMyError() throws(MyError) {} - // expected-error@-1{{@objc functions cannot have typed throw}} + // 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{{@objc functions cannot have typed throw}} + // 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 fb3f97de91061..107bba5f9094b 100644 --- a/test/attr/attr_objc_async.swift +++ b/test/attr/attr_objc_async.swift @@ -56,7 +56,7 @@ actor MyActor { @objc func objcAsyncThrowsError() async throws(Error) -> Void {} @objc func objcAsyncThrowsObjCError() async throws(ObjCError) -> Void {} - // expected-error@-1 {{@objc functions cannot have typed throw}} + // expected-error@-1 {{typed 'throws' instance method cannot be represented in Objective-C}} } actor class MyActor2 { }