Skip to content

Commit 8ecb83e

Browse files
committed
Sema: Diagnose unsupported '@objc' on classes and members of extensions of classes with resilient ancestry
Unless -enable-resilient-objc-class-stubs is passed in, these cases are not supported, so now we diagnose them instead of asserting or failing to link. Note the behavior change here; classes with resilient ancestry were previously isObjC(). However this is wrong since isObjC() means "statically visible to Objective-C via the generated header". After this patch, isObjC() only returns true for a class with resilient ancestry if -enable-resilient-objc-class-stubs is passed in.
1 parent 68c0762 commit 8ecb83e

File tree

6 files changed

+137
-9
lines changed

6 files changed

+137
-9
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3592,9 +3592,11 @@ ERROR(invalid_nonobjc_extension,none,
35923592
ERROR(objc_in_extension_context,none,
35933593
"members of constrained extensions cannot be declared @objc", ())
35943594
ERROR(objc_in_generic_extension,none,
3595-
"members of extensions of "
3596-
"%select{classes from generic context|generic classes}0 "
3597-
"cannot be declared @objc", (bool))
3595+
"extensions of %select{classes from generic context|generic classes}0 "
3596+
"cannot contain '@objc' members", (bool))
3597+
ERROR(objc_in_resilient_extension,none,
3598+
"extensions of classes built with library evolution support "
3599+
"cannot contain '@objc' members", ())
35983600
ERROR(objc_operator, none,
35993601
"operator methods cannot be declared @objc", ())
36003602
ERROR(objc_operator_proto, none,
@@ -3615,6 +3617,10 @@ NOTE(objc_inference_swift3_addnonobjc,none,
36153617
ERROR(objc_for_generic_class,none,
36163618
"generic subclasses of '@objc' classes cannot have an explicit '@objc' "
36173619
"because they are not directly visible from Objective-C", ())
3620+
ERROR(objc_for_resilient_class,none,
3621+
"classes built with library evolution support cannot have explicit "
3622+
"'@objc' subclasses because they are not directly "
3623+
"visible from Objective-C", ())
36183624
ERROR(objc_getter_for_nonobjc_property,none,
36193625
"'@objc' getter for non-'@objc' property", ())
36203626
ERROR(objc_getter_for_nonobjc_subscript,none,

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,21 @@ static bool checkObjCInExtensionContext(const ValueDecl *value,
383383
}
384384

385385
if (auto classDecl = ED->getSelfClassDecl()) {
386+
auto *mod = value->getModuleContext();
387+
auto &ctx = mod->getASTContext();
388+
389+
if (!ctx.LangOpts.EnableObjCResilientClassStubs) {
390+
if (classDecl->checkAncestry().contains(
391+
AncestryFlags::ResilientOther) ||
392+
classDecl->hasResilientMetadata(mod,
393+
ResilienceExpansion::Maximal)) {
394+
if (diagnose) {
395+
value->diagnose(diag::objc_in_resilient_extension);
396+
}
397+
return true;
398+
}
399+
}
400+
386401
if (classDecl->isGenericContext()) {
387402
if (!classDecl->usesObjCGenericsModel()) {
388403
if (diagnose) {
@@ -1016,6 +1031,20 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
10161031
.fixItRemove(attr->getRangeWithAt());
10171032
}
10181033

1034+
// If the class has resilient ancestry, @objc just controls the runtime
1035+
// name unless -enable-resilient-objc-class-stubs is enabled.
1036+
if (ancestry.contains(AncestryFlags::ResilientOther) &&
1037+
!ctx.LangOpts.EnableObjCResilientClassStubs) {
1038+
if (attr->hasName()) {
1039+
const_cast<ClassDecl *>(CD)->getAttrs().add(
1040+
new (ctx) ObjCRuntimeNameAttr(*attr));
1041+
return None;
1042+
}
1043+
1044+
ctx.Diags.diagnose(attr->getLocation(), diag::objc_for_resilient_class)
1045+
.fixItRemove(attr->getRangeWithAt());
1046+
}
1047+
10191048
// Only allow ObjC-rooted classes to be @objc.
10201049
// (Leave a hole for test cases.)
10211050
if (ancestry.contains(AncestryFlags::ObjC) &&
@@ -1032,8 +1061,16 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
10321061
return ObjCReason(ObjCReason::ExplicitlyObjC);
10331062
}
10341063

1035-
if (ancestry.contains(AncestryFlags::ObjC) &&
1036-
!ancestry.contains(AncestryFlags::Generic)) {
1064+
if (ancestry.contains(AncestryFlags::ObjC)) {
1065+
if (ancestry.contains(AncestryFlags::Generic)) {
1066+
return None;
1067+
}
1068+
1069+
if (ancestry.contains(AncestryFlags::ResilientOther) &&
1070+
!ctx.LangOpts.EnableObjCResilientClassStubs) {
1071+
return None;
1072+
}
1073+
10371074
return ObjCReason(ObjCReason::ImplicitlyObjC);
10381075
}
10391076

test/attr/attr_objc.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,9 @@ extension subject_genericClass where T : Hashable {
193193
}
194194

195195
extension subject_genericClass {
196-
@objc var extProp: Int { return 0 } // expected-error{{members of extensions of generic classes cannot be declared @objc}}
196+
@objc var extProp: Int { return 0 } // expected-error{{extensions of generic classes cannot contain '@objc' members}}
197197

198-
@objc func extFoo() {} // expected-error{{members of extensions of generic classes cannot be declared @objc}}
198+
@objc func extFoo() {} // expected-error{{extensions of generic classes cannot contain '@objc' members}}
199199
}
200200

201201
@objc

test/attr/attr_objc_resilience.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module-path %t/resilient_struct.swiftmodule %S/../Inputs/resilient_struct.swift -enable-library-evolution
3+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module-path %t/resilient_objc_class.swiftmodule %S/../Inputs/resilient_objc_class.swift -I %t -enable-library-evolution
4+
// RUN: %target-swift-frontend -typecheck -verify %s -I %t
5+
6+
// REQUIRES: objc_interop
7+
8+
import Foundation
9+
import resilient_objc_class
10+
11+
@objc public class ResilientNSObjectSubclass : ResilientNSObjectOutsideParent {}
12+
// expected-error@-1 {{classes built with library evolution support cannot have explicit '@objc' subclasses because they are not directly visible from Objective-C}}
13+
14+
public class AnotherResilientNSObjectSubclass : ResilientNSObjectOutsideParent {}
15+
16+
extension ResilientNSObjectOutsideParent {
17+
@objc public func categoryOneMethod() {}
18+
// expected-error@-1 {{extensions of classes built with library evolution support cannot contain '@objc' members}}
19+
}
20+
21+
extension AnotherResilientNSObjectSubclass {
22+
@objc public func categoryTwoMethod() {}
23+
// expected-error@-1 {{extensions of classes built with library evolution support cannot contain '@objc' members}}
24+
}
25+
26+
// Note: @_fixed_layout on a class only applies to the storage layout and
27+
// not metadata, which remains resilient.
28+
29+
@_fixed_layout
30+
@objc public class FixedLayoutNSObjectSubclass : FixedLayoutNSObjectOutsideParent {}
31+
// expected-error@-1 {{classes built with library evolution support cannot have explicit '@objc' subclasses because they are not directly visible from Objective-C}}
32+
33+
@_fixed_layout
34+
public class AnotherFixedLayoutNSObjectSubclass : FixedLayoutNSObjectOutsideParent {}
35+
36+
extension FixedLayoutNSObjectOutsideParent {
37+
@objc public func categoryOneMethod() {}
38+
// expected-error@-1 {{extensions of classes built with library evolution support cannot contain '@objc' members}}
39+
}
40+
41+
extension AnotherFixedLayoutNSObjectSubclass {
42+
@objc public func categoryTwoMethod() {}
43+
// expected-error@-1 {{extensions of classes built with library evolution support cannot contain '@objc' members}}
44+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module-path %t/resilient_struct.swiftmodule %S/../Inputs/resilient_struct.swift -enable-library-evolution
3+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module-path %t/resilient_objc_class.swiftmodule %S/../Inputs/resilient_objc_class.swift -I %t -enable-library-evolution -enable-resilient-objc-class-stubs
4+
// RUN: %target-swift-frontend -typecheck -verify %s -I %t -enable-resilient-objc-class-stubs
5+
6+
// REQUIRES: objc_interop
7+
8+
import Foundation
9+
import resilient_objc_class
10+
11+
// When built with -enable-resilient-objc-class-stubs, all of these cases are
12+
// allowed.
13+
14+
@objc public class ResilientNSObjectSubclass : ResilientNSObjectOutsideParent {}
15+
16+
public class AnotherResilientNSObjectSubclass : ResilientNSObjectOutsideParent {}
17+
18+
extension ResilientNSObjectOutsideParent {
19+
@objc public func categoryOneMethod() {}
20+
}
21+
22+
extension AnotherResilientNSObjectSubclass {
23+
@objc public func categoryTwoMethod() {}
24+
}
25+
26+
// Note: @_fixed_layout on a class only applies to the storage layout and
27+
// not metadata, which remains resilient.
28+
29+
@_fixed_layout
30+
@objc public class FixedLayoutNSObjectSubclass : FixedLayoutNSObjectOutsideParent {}
31+
32+
@_fixed_layout
33+
public class AnotherFixedLayoutNSObjectSubclass : FixedLayoutNSObjectOutsideParent {}
34+
35+
extension FixedLayoutNSObjectOutsideParent {
36+
@objc public func categoryOneMethod() {}
37+
}
38+
39+
extension AnotherFixedLayoutNSObjectSubclass {
40+
@objc public func categoryTwoMethod() {}
41+
}

test/decl/ext/extension-generic-objc.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class A<T> : NSObject {
1515
}
1616
extension A {
1717
// This should throw an error
18-
@objc func a1() {} // expected-error{{members of extensions of generic classes cannot be declared @objc}}
18+
@objc func a1() {} // expected-error{{extensions of generic classes cannot contain '@objc' members}}
1919
// This should *not* throw an error
2020
func a2() {}
2121
}
@@ -51,6 +51,6 @@ class Outer<T> {
5151

5252
extension Outer.Inner {
5353
@objc func outerInner1() {}
54-
// expected-error@-1{{members of extensions of classes from generic context cannot be declared @objc}}
54+
// expected-error@-1{{extensions of classes from generic context cannot contain '@objc' members}}
5555
func outerInner2() {}
5656
}

0 commit comments

Comments
 (0)