Skip to content

Commit 117e5f0

Browse files
authored
Merge pull request swiftlang#8496 from DougGregor/se-0160-objc-nonobjc-extensions
2 parents 288cb35 + e1a4700 commit 117e5f0

File tree

7 files changed

+65
-9
lines changed

7 files changed

+65
-9
lines changed

include/swift/AST/Attr.def

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ SIMPLE_DECL_ATTR(final, Final,
9292
OnClass | OnFunc | OnVar | OnSubscript|DeclModifier, 2)
9393

9494
DECL_ATTR(objc, ObjC,
95-
OnFunc | OnClass | OnProtocol | OnVar | OnSubscript |
95+
OnFunc | OnClass | OnProtocol | OnExtension | OnVar | OnSubscript |
9696
OnConstructor | OnDestructor | OnEnum | OnEnumElement, 3)
9797

9898
SIMPLE_DECL_ATTR(required, Required,
@@ -158,7 +158,7 @@ DECL_ATTR(autoclosure, AutoClosure, OnParam, 28)
158158
SIMPLE_DECL_ATTR(noescape, NoEscape, OnParam, 29)
159159

160160
SIMPLE_DECL_ATTR(nonobjc, NonObjC,
161-
OnFunc | OnVar | OnSubscript | OnConstructor, 30)
161+
OnExtension | OnFunc | OnVar | OnSubscript | OnConstructor, 30)
162162

163163
SIMPLE_DECL_ATTR(_fixed_layout, FixedLayout,
164164
OnVar | OnClass | OnStruct | OnEnum | UserInaccessible, 31)

include/swift/AST/DiagnosticsSema.def

+9-3
Original file line numberDiff line numberDiff line change
@@ -2983,13 +2983,16 @@ ERROR(invalid_objc_decl_context,none,
29832983
"@objc can only be used with members of classes, @objc protocols, and "
29842984
"concrete extensions of classes", ())
29852985
ERROR(invalid_objc_decl,none,
2986-
"only classes, protocols, methods, initializers, properties, and "
2987-
"subscript declarations can be declared @objc", ())
2986+
"only classes (and their extensions), protocols, methods, initializers, "
2987+
"properties, and subscript declarations can be declared @objc", ())
29882988
ERROR(invalid_objc_swift_rooted_class,none,
29892989
"only classes that inherit from NSObject can be declared @objc", ())
29902990
ERROR(invalid_nonobjc_decl,none,
29912991
"only methods, initializers, properties and subscript declarations can "
29922992
"be declared @nonobjc", ())
2993+
ERROR(invalid_nonobjc_extension,none,
2994+
"only extensions of classes can be declared @nonobjc", ())
2995+
29932996
ERROR(objc_in_extension_context,none,
29942997
"members of constrained extensions cannot be declared @objc", ())
29952998
ERROR(objc_in_generic_extension,none,
@@ -3050,8 +3053,11 @@ ERROR(objc_enum_case_req_objc_enum,none,
30503053
ERROR(objc_enum_case_multi,none,
30513054
"'@objc' enum case declaration defines multiple enum cases with the same Objective-C name", ())
30523055

3056+
ERROR(objc_extension_not_class,none,
3057+
"'@objc' can only be applied to an extension of a class", ())
3058+
30533059
// If you change this, also change enum ObjCReason
3054-
#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @IBOutlet|marked @IBAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable}"
3060+
#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @IBOutlet|marked @IBAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable|in an @objc extension of a class (without @nonobjc)}"
30553061

30563062
ERROR(objc_invalid_on_var,none,
30573063
"property cannot be %" OBJC_ATTR_SELECT "0 "

lib/Sema/TypeCheckDecl.cpp

+25-2
Original file line numberDiff line numberDiff line change
@@ -2285,6 +2285,15 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
22852285
}
22862286
}
22872287

2288+
/// Whether this declaration is a member of a class extension marked @objc.
2289+
static bool isMemberOfObjCClassExtension(const ValueDecl *VD) {
2290+
auto ext = dyn_cast<ExtensionDecl>(VD->getDeclContext());
2291+
if (!ext) return false;
2292+
2293+
return ext->getAsClassOrClassExtensionContext() &&
2294+
ext->getAttrs().hasAttribute<ObjCAttr>();
2295+
}
2296+
22882297
/// Whether this declaration is a member of a class with the `@objcMembers`
22892298
/// attribute.
22902299
static bool isMemberOfObjCMembersClass(const ValueDecl *VD) {
@@ -2341,8 +2350,13 @@ static Optional<ObjCReason> shouldMarkAsObjC(TypeChecker &TC,
23412350
return ObjCReason::MemberOfObjCProtocol;
23422351
// A @nonobjc is not @objc, even if it is an override of an @objc, so check
23432352
// for @nonobjc first.
2344-
if (VD->getAttrs().hasAttribute<NonObjCAttr>())
2353+
if (VD->getAttrs().hasAttribute<NonObjCAttr>() ||
2354+
(isa<ExtensionDecl>(VD->getDeclContext()) &&
2355+
cast<ExtensionDecl>(VD->getDeclContext())->getAttrs()
2356+
.hasAttribute<NonObjCAttr>()))
23452357
return None;
2358+
if (isMemberOfObjCClassExtension(VD))
2359+
return ObjCReason::MemberOfObjCExtension;
23462360
if (isMemberOfObjCMembersClass(VD) && canInferImplicitObjC())
23472361
return ObjCReason::MemberOfObjCMembersClass;
23482362
// An override of an @objc declaration is implicitly @objc.
@@ -8404,6 +8418,9 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
84048418
if (isa<ClassDecl>(D) ||
84058419
isa<ProtocolDecl>(D)) {
84068420
/* ok */
8421+
} else if (auto Ext = dyn_cast<ExtensionDecl>(D)) {
8422+
if (!Ext->getAsClassOrClassExtensionContext())
8423+
error = diag::objc_extension_not_class;
84078424
} else if (auto ED = dyn_cast<EnumDecl>(D)) {
84088425
if (ED->isGenericContext())
84098426
error = diag::objc_enum_generic;
@@ -8501,7 +8518,8 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
85018518
}
85028519

85038520
if (auto nonObjcAttr = Attrs.getAttribute<NonObjCAttr>()) {
8504-
// Only methods, properties, subscripts and constructors can be NonObjC.
8521+
// Only extensions of classes; methods, properties, subscripts
8522+
// and constructors can be NonObjC.
85058523
// The last three are handled automatically by generic attribute
85068524
// validation -- for the first one, we have to check FuncDecls
85078525
// ourselves.
@@ -8515,6 +8533,11 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
85158533
error = diag::invalid_nonobjc_decl;
85168534
}
85178535

8536+
if (auto ext = dyn_cast<ExtensionDecl>(D)) {
8537+
if (!ext->getAsClassOrClassExtensionContext())
8538+
error = diag::invalid_nonobjc_extension;
8539+
}
8540+
85188541
if (error) {
85198542
TC.diagnose(D->getStartLoc(), *error)
85208543
.fixItRemove(nonObjcAttr->getRangeWithAt());

lib/Sema/TypeChecker.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,8 @@ enum class ObjCReason {
539539
ExplicitlyIBInspectable,
540540
/// Has an explicit '@GKInspectable' attribute.
541541
ExplicitlyGKInspectable,
542+
/// Is it a member of an @objc extension of a class.
543+
MemberOfObjCExtension,
542544
/// Is it a member of an @objcMembers class.
543545
MemberOfObjCMembersClass,
544546
/// A member of an Objective-C-defined class or subclass.
@@ -547,7 +549,8 @@ enum class ObjCReason {
547549
Accessor,
548550
};
549551

550-
/// Determine whether we should diagnose conflicts due yo
552+
/// Determine whether we should diagnose conflicts due to inferring @objc
553+
/// with this particular reason.
551554
static inline bool shouldDiagnoseObjCReason(ObjCReason reason) {
552555
switch(reason) {
553556
case ObjCReason::ExplicitlyCDecl:
@@ -562,6 +565,7 @@ static inline bool shouldDiagnoseObjCReason(ObjCReason reason) {
562565
case ObjCReason::ImplicitlyObjC:
563566
case ObjCReason::ExplicitlyIBInspectable:
564567
case ObjCReason::ExplicitlyGKInspectable:
568+
case ObjCReason::MemberOfObjCExtension:
565569
return true;
566570

567571
case ObjCReason::MemberOfObjCSubclass:
@@ -587,6 +591,7 @@ static inline unsigned getObjCDiagnosticAttrKind(ObjCReason reason) {
587591
case ObjCReason::ImplicitlyObjC:
588592
case ObjCReason::ExplicitlyIBInspectable:
589593
case ObjCReason::ExplicitlyGKInspectable:
594+
case ObjCReason::MemberOfObjCExtension:
590595
return static_cast<unsigned>(reason);
591596

592597
case ObjCReason::MemberOfObjCSubclass:

test/attr/attr_nonobjc.swift

+3
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,6 @@ class NSManagedAndNonObjCNotAllowed {
102102
}
103103
}
104104
}
105+
106+
struct SomeStruct { }
107+
@nonobjc extension SomeStruct { } // expected-error{{only extensions of classes can be declared @nonobjc}}

test/attr/attr_objc.swift

+12-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ protocol Protocol_Class2 : class {}
3030

3131
//===--- Subjects of @objc attribute.
3232

33-
@objc extension PlainClass { } // expected-error{{@objc cannot be applied to this declaration}}{{1-7=}}
33+
@objc extension PlainStruct { } // expected-error{{'@objc' can only be applied to an extension of a class}}{{1-7=}}
3434

3535
@objc
3636
var subject_globalVar: Int // expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}}
@@ -2159,6 +2159,17 @@ class ConformsToProtocolThrowsObjCName2 : ProtocolThrowsObjCName {
21592159
@objc func func_dictionary2b(x: Dictionary<String, Int>) { }
21602160
}
21612161

2162+
@objc extension PlainClass {
2163+
// CHECK-LABEL: @objc final func objc_ext_objc_okay(_: Int) {
2164+
final func objc_ext_objc_okay(_: Int) { }
2165+
2166+
final func objc_ext_objc_not_okay(_: PlainStruct) { }
2167+
// expected-error@-1{{method cannot be in an @objc extension of a class (without @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
2168+
// expected-note@-2 {{Swift structs cannot be represented in Objective-C}}
2169+
2170+
// CHECK-LABEL: {{^}} @nonobjc final func objc_ext_objc_explicit_nonobjc(_: PlainStruct) {
2171+
@nonobjc final func objc_ext_objc_explicit_nonobjc(_: PlainStruct) { }
2172+
}
21622173

21632174
@objc class ObjC_Class1 : Hashable {
21642175
var hashValue: Int { return 0 }

test/attr/attr_objcMembers.swift

+8
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,11 @@ func selectorTest() {
3131
_ = #selector(SubClassOfSomeClassWithObjCMembers.baz)
3232
_ = #selector(SubClassOfSomeClassWithObjCMembers.wibble)
3333
}
34+
35+
@nonobjc extension SomeClassWithObjCMembers {
36+
func notInferredObjC() { } // expected-note{{add '@objc' to expose this instance method to Objective-C}}
37+
}
38+
39+
func selectorTestFail() {
40+
_ = #selector(SomeClassWithObjCMembers.notInferredObjC) // expected-error{{argument of '#selector' refers to instance method 'notInferredObjC()' that is not exposed to Objective-C}}
41+
}

0 commit comments

Comments
 (0)