Skip to content

Enable @objc @implementation (SE-0436) as an ordinary language feature #74801

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

Merged
merged 2 commits into from
Jun 28, 2024
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
14 changes: 7 additions & 7 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1767,16 +1767,16 @@ ERROR(attr_objc_implementation_must_be_unconditional,none,
"only unconditional extensions can implement an Objective-C '@interface'",
())
ERROR(attr_objc_implementation_must_be_imported,none,
"'@_objcImplementation' cannot be used to extend %kind0 because it was "
"'@objc @implementation' cannot be used to extend %kind0 because it was "
"defined by a Swift 'class' declaration, not an imported Objective-C "
"'@interface' declaration",
(ValueDecl *))
ERROR(attr_objc_implementation_must_have_super,none,
"'@_objcImplementation' cannot be used to implement root %kind0; declare "
"its superclass in the header",
"'@objc @implementation' cannot be used to implement root %kind0; "
"declare its superclass in the header",
(ValueDecl *))
ERROR(objc_implementation_cannot_have_generics,none,
"'@_objcImplementation' cannot be used to implement %kind0",
"'@objc @implementation' cannot be used to implement %kind0",
(ValueDecl *))
ERROR(attr_objc_implementation_category_not_found,none,
"could not find category %0 on Objective-C class %1; make sure your "
Expand All @@ -1794,7 +1794,7 @@ ERROR(attr_objc_implementation_no_category_for_func,none,
"name from this attribute",
(ValueDecl*))
ERROR(attr_objc_implementation_no_conformance,none,
"'@_objcImplementation' extension cannot add conformance to %0; "
"'@objc @implementation' extension cannot add conformance to %0; "
"add this conformance %select{with an ordinary extension|"
"in the Objective-C header}1",
(Type, bool))
Expand Down Expand Up @@ -1922,7 +1922,7 @@ NOTE(objc_implementation_requirement_here,none,
(ValueDecl *))

ERROR(objc_implementation_init_must_be_convenience, none,
"%kind0 is not valid in an '@_objcImplementation' extension because "
"%kind0 is not valid in an '@objc @implementation' extension because "
"Objective-C subclasses must be able to override "
"%select{designated|required}1 initializers",
(const ValueDecl *, /*isRequired=*/bool))
Expand All @@ -1936,7 +1936,7 @@ NOTE(objc_implementation_init_turn_required_to_convenience, none,

// Fallback diagnostic; super-general by nature.
ERROR(objc_implementation_member_requires_vtable, none,
"%kind0 is not valid in an '@_objcImplementation' extension because it "
"%kind0 is not valid in an '@objc @implementation' extension because it "
"is an overridable Swift-only %kindonly0",
(const ValueDecl *))

Expand Down
4 changes: 1 addition & 3 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ SUPPRESSIBLE_LANGUAGE_FEATURE(SendingArgsAndResults, 430, "Sending arg and resul
LANGUAGE_FEATURE(BorrowingSwitch, 432, "Noncopyable type pattern matching")
CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(IsolatedAny, 431, "@isolated(any) function types")
LANGUAGE_FEATURE(IsolatedAny2, 431, "@isolated(any) function types")
LANGUAGE_FEATURE(ObjCImplementation, 436, "@objc @implementation extensions")

// Swift 6
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
Expand Down Expand Up @@ -378,9 +379,6 @@ EXPERIMENTAL_FEATURE(ClosureIsolation, true)
// Whether lookup of members respects the enclosing file's imports.
EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE(MemberImportVisibility, true)

// Enable @implementation on extensions of ObjC classes.
EXPERIMENTAL_FEATURE(ObjCImplementation, true)

// Enable @implementation on extensions of ObjC classes with non-fixed layout
// due to resilient stored properties. Requires OS support; this flag exists for
// staging purposes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -target %target-stable-abi-triple -I %S/Inputs/abi -F %clang-importer-sdk-path/frameworks %s -import-objc-header %S/Inputs/objc_implementation.h -emit-ir -o %t.ir -enable-library-evolution -enable-experimental-feature ObjCImplementationWithResilientStorage -target %target-future-triple
// REQUIRES: objc_interop

@_objcImplementation extension ImplClassWithResilientStoredProperty {
@objc @implementation extension ImplClassWithResilientStoredProperty {
@objc var beforeInt: Int32 = 0 // no-error
final var a: Mirror? // expected-error {{does not support stored properties whose size can change due to library evolution; store this value in an object or 'any' type}}
final var b: AnyKeyPath? // no-error
Expand Down
16 changes: 8 additions & 8 deletions test/decl/ext/objc_implementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ protocol EmptySwiftProto {}
// FIXME: give better diagnostic expected-error@-4 {{extension for main class interface should provide implementation for property 'propertyFromHeader8'}}
// FIXME: give better diagnostic expected-error@-5 {{extension for main class interface should provide implementation for property 'propertyFromHeader7'}}
// FIXME: give better diagnostic expected-error@-6 {{extension for main class interface should provide implementation for instance method 'method(fromHeader3:)'}}
// expected-error@-7 {{'@_objcImplementation' extension cannot add conformance to 'EmptySwiftProto'; add this conformance with an ordinary extension}}
// expected-error@-8 {{'@_objcImplementation' extension cannot add conformance to 'EmptyObjCProto'; add this conformance in the Objective-C header}}
// expected-error@-7 {{'@objc @implementation' extension cannot add conformance to 'EmptySwiftProto'; add this conformance with an ordinary extension}}
// expected-error@-8 {{'@objc @implementation' extension cannot add conformance to 'EmptyObjCProto'; add this conformance in the Objective-C header}}
// expected-error@-9 {{extension for main class interface should provide implementation for instance method 'extensionMethod(fromHeader2:)'}}

func method(fromHeader1: CInt) {
Expand Down Expand Up @@ -207,12 +207,12 @@ protocol EmptySwiftProto {}
}

@nonobjc public init(notFromHeader4: CInt) {
// expected-error@-1 {{initializer 'init(notFromHeader4:)' is not valid in an '@_objcImplementation' extension because Objective-C subclasses must be able to override designated initializers}}
// expected-error@-1 {{initializer 'init(notFromHeader4:)' is not valid in an '@objc @implementation' extension because Objective-C subclasses must be able to override designated initializers}}
// expected-note@-2 {{add 'convenience' keyword to make this a convenience initializer}} {{12-12=convenience }}
}

@nonobjc public required init(notFromHeader5: CInt) {
// expected-error@-1 {{initializer 'init(notFromHeader5:)' is not valid in an '@_objcImplementation' extension because Objective-C subclasses must be able to override required initializers}}
// expected-error@-1 {{initializer 'init(notFromHeader5:)' is not valid in an '@objc @implementation' extension because Objective-C subclasses must be able to override required initializers}}
// expected-note@-2 {{replace 'required' keyword with 'convenience' to make this a convenience initializer}} {{19-27=convenience}}
}

Expand Down Expand Up @@ -478,17 +478,17 @@ protocol EmptySwiftProto {}
// expected-note@-1 2 {{'SwiftClass' declared here}}

@objc @implementation extension SwiftClass {}
// expected-error@-1 {{'@_objcImplementation' cannot be used to extend class 'SwiftClass' because it was defined by a Swift 'class' declaration, not an imported Objective-C '@interface' declaration}} {{7-23=}}
// expected-error@-1 {{'@objc @implementation' cannot be used to extend class 'SwiftClass' because it was defined by a Swift 'class' declaration, not an imported Objective-C '@interface' declaration}} {{7-23=}}

@objc(WTF) @implementation extension SwiftClass {} // expected
// expected-error@-1 {{'@_objcImplementation' cannot be used to extend class 'SwiftClass' because it was defined by a Swift 'class' declaration, not an imported Objective-C '@interface' declaration}} {{12-28=}}
// expected-error@-1 {{'@objc @implementation' cannot be used to extend class 'SwiftClass' because it was defined by a Swift 'class' declaration, not an imported Objective-C '@interface' declaration}} {{12-28=}}

@objc @implementation extension ObjCImplRootClass {
// expected-error@-1 {{'@_objcImplementation' cannot be used to implement root class 'ObjCImplRootClass'; declare its superclass in the header}}
// expected-error@-1 {{'@objc @implementation' cannot be used to implement root class 'ObjCImplRootClass'; declare its superclass in the header}}
}

@objc @implementation extension ObjCImplGenericClass {
// expected-error@-1 {{'@_objcImplementation' cannot be used to implement generic class 'ObjCImplGenericClass'}}
// expected-error@-1 {{'@objc @implementation' cannot be used to implement generic class 'ObjCImplGenericClass'}}
}

@implementation extension ObjCBadClass {
Expand Down
4 changes: 2 additions & 2 deletions test/decl/ext/objc_implementation_conflicts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import objc_implementation_private
#endif

@_objcImplementation extension ObjCClass {
@objc @implementation extension ObjCClass {
@objc func method(fromHeader1: CInt) {
// OK, provides an implementation for the header's method.
}
Expand Down Expand Up @@ -179,7 +179,7 @@ import objc_implementation_private
let rdar122280735: (@escaping () -> ()) -> Void = { _ in }
}

@_objcImplementation(PresentAdditions) extension ObjCClass {
@objc(PresentAdditions) @implementation extension ObjCClass {
@objc func categoryMethod(fromHeader3: CInt) {
// OK
}
Expand Down
16 changes: 8 additions & 8 deletions test/decl/ext/objc_implementation_early_adopter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ protocol EmptySwiftProto {}
// FIXME: give better diagnostic expected-warning@-4 {{extension for main class interface should provide implementation for property 'propertyFromHeader8'}}
// FIXME: give better diagnostic expected-warning@-5 {{extension for main class interface should provide implementation for property 'propertyFromHeader7'}}
// FIXME: give better diagnostic expected-warning@-6 {{extension for main class interface should provide implementation for instance method 'method(fromHeader3:)'}}
// expected-warning@-7 {{'@_objcImplementation' extension cannot add conformance to 'EmptySwiftProto'; add this conformance with an ordinary extension}}
// expected-warning@-8 {{'@_objcImplementation' extension cannot add conformance to 'EmptyObjCProto'; add this conformance in the Objective-C header}}
// expected-warning@-7 {{'@objc @implementation' extension cannot add conformance to 'EmptySwiftProto'; add this conformance with an ordinary extension}}
// expected-warning@-8 {{'@objc @implementation' extension cannot add conformance to 'EmptyObjCProto'; add this conformance in the Objective-C header}}
// expected-warning@-9 {{extension for main class interface should provide implementation for instance method 'extensionMethod(fromHeader2:)'}}

func method(fromHeader1: CInt) {
Expand Down Expand Up @@ -204,12 +204,12 @@ protocol EmptySwiftProto {}
}

@nonobjc public init(notFromHeader4: CInt) {
// expected-warning@-1 {{initializer 'init(notFromHeader4:)' is not valid in an '@_objcImplementation' extension because Objective-C subclasses must be able to override designated initializers}}
// expected-warning@-1 {{initializer 'init(notFromHeader4:)' is not valid in an '@objc @implementation' extension because Objective-C subclasses must be able to override designated initializers}}
// expected-note@-2 {{add 'convenience' keyword to make this a convenience initializer}} {{12-12=convenience }}
}

@nonobjc public required init(notFromHeader5: CInt) {
// expected-warning@-1 {{initializer 'init(notFromHeader5:)' is not valid in an '@_objcImplementation' extension because Objective-C subclasses must be able to override required initializers}}
// expected-warning@-1 {{initializer 'init(notFromHeader5:)' is not valid in an '@objc @implementation' extension because Objective-C subclasses must be able to override required initializers}}
// expected-note@-2 {{replace 'required' keyword with 'convenience' to make this a convenience initializer}} {{19-27=convenience}}
}

Expand Down Expand Up @@ -477,17 +477,17 @@ protocol EmptySwiftProto {}
// expected-note@-1 2 {{'SwiftClass' declared here}}

@_objcImplementation extension SwiftClass {}
// expected-error@-1 {{'@_objcImplementation' cannot be used to extend class 'SwiftClass' because it was defined by a Swift 'class' declaration, not an imported Objective-C '@interface' declaration}} {{1-22=}}
// expected-error@-1 {{'@objc @implementation' cannot be used to extend class 'SwiftClass' because it was defined by a Swift 'class' declaration, not an imported Objective-C '@interface' declaration}} {{1-22=}}

@_objcImplementation(WTF) extension SwiftClass {} // expected
// expected-error@-1 {{'@_objcImplementation' cannot be used to extend class 'SwiftClass' because it was defined by a Swift 'class' declaration, not an imported Objective-C '@interface' declaration}} {{1-27=}}
// expected-error@-1 {{'@objc @implementation' cannot be used to extend class 'SwiftClass' because it was defined by a Swift 'class' declaration, not an imported Objective-C '@interface' declaration}} {{1-27=}}

@_objcImplementation extension ObjCImplRootClass {
// expected-error@-1 {{'@_objcImplementation' cannot be used to implement root class 'ObjCImplRootClass'; declare its superclass in the header}}
// expected-error@-1 {{'@objc @implementation' cannot be used to implement root class 'ObjCImplRootClass'; declare its superclass in the header}}
}

@_objcImplementation extension ObjCImplGenericClass {
// expected-error@-1 {{'@_objcImplementation' cannot be used to implement generic class 'ObjCImplGenericClass'}}
// expected-error@-1 {{'@objc @implementation' cannot be used to implement generic class 'ObjCImplGenericClass'}}
}

//
Expand Down
11 changes: 4 additions & 7 deletions test/decl/ext/objc_implementation_features.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
// REQUIRES: objc_interop

// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck %s -import-objc-header %S/Inputs/objc_implementation.h 2>&1 | %FileCheck --check-prefixes NO,CHECK %s
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck %s -import-objc-header %S/Inputs/objc_implementation.h -enable-experimental-feature ObjCImplementation 2>&1 | %FileCheck --check-prefixes YES,CHECK %s
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck %s -import-objc-header %S/Inputs/objc_implementation.h 2>&1 | %FileCheck %s

// NO-NOT: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: warning: '@_objcImplementation' is deprecated; use '@implementation' instead
// YES-DAG: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: warning: '@_objcImplementation' is deprecated; use '@implementation' instead
// CHECK-DAG: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: warning: '@_objcImplementation' is deprecated; use '@implementation' instead
@_objcImplementation(EmptyCategory) extension ObjCClass {}

// CHECK-DAG: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: warning: extension for main class interface should provide implementation for instance method 'subclassMethod(fromHeader1:)'; this will become an error after adopting '@implementation'
// CHECK-NOT: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: warning: '@_objcImplementation' is deprecated; use '@implementation' instead
@_objcImplementation extension ObjCSubclass {}

// CHECK-DAG: objc_implementation_features.swift:[[@LINE+3]]:{{[0-9]+}}: error: extension for main class interface should provide implementation for initializer 'init()'{{$}}
// NO-DAG: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: error: 'implementation' attribute is only valid when experimental feature ObjCImplementation is enabled
// YES-NOT: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: error: 'implementation' attribute is only valid when experimental feature ObjCImplementation is enabled
// CHECK-DAG: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: error: extension for main class interface should provide implementation for initializer 'init()'{{$}}
// CHECK-NOT: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: error: 'implementation' attribute is only valid when experimental feature ObjCImplementation is enabled
@objc @implementation extension ObjCBasicInitClass {}
2 changes: 1 addition & 1 deletion test/decl/ext/objc_implementation_impl_only.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

@_implementationOnly import objc_implementation_internal

@_objcImplementation extension InternalObjCClass {
@objc @implementation extension InternalObjCClass {
@objc public func method(fromHeader1: CInt) {
// OK
}
Expand Down