Skip to content

Commit 0678a38

Browse files
committed
Require objcImplementation members to avoid vtable
They either must be @objc dynamic (the dynamic is implicit) or final.
1 parent e544c21 commit 0678a38

File tree

4 files changed

+86
-2
lines changed

4 files changed

+86
-2
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,18 @@ NOTE(attr_objc_implementation_fixit_remove_category_name,none,
15121512
"remove arguments to implement the main '@interface' for this class",
15131513
())
15141514

1515+
ERROR(member_of_objc_implementation_not_objc_or_final,none,
1516+
"%0 %1 does not match any %0 declared in the headers for %2; did you use "
1517+
"the %0's Swift name?",
1518+
(DescriptiveDeclKind, ValueDecl *, ValueDecl *))
1519+
NOTE(fixit_add_objc_for_objc_implementation,none,
1520+
"add '@objc' to define an Objective-C-compatible %0 not declared in "
1521+
"the header",
1522+
(DescriptiveDeclKind))
1523+
NOTE(fixit_add_final_for_objc_implementation,none,
1524+
"add 'final' to define a Swift %0 that cannot be overridden",
1525+
(DescriptiveDeclKind))
1526+
15151527
ERROR(cdecl_not_at_top_level,none,
15161528
"@_cdecl can only be applied to global functions", ())
15171529
ERROR(cdecl_empty_name,none,

lib/Sema/TypeCheckDecl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,12 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
987987
if (isa<ExtensionDecl>(dc) && dc->getSelfClassDecl())
988988
return true;
989989

990+
// @objc declarations in @_objcImplementation extensions are implicitly
991+
// dynamic.
992+
if (auto ED = dyn_cast_or_null<ExtensionDecl>(dc->getAsDecl()))
993+
if (ED->isObjCImplementation())
994+
return true;
995+
990996
// If any of the declarations overridden by this declaration are dynamic
991997
// or were imported from Objective-C, this declaration is dynamic.
992998
// Don't do this if the declaration is not exposed to Objective-C; that's

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,29 @@ static void checkDynamicSelfType(ValueDecl *decl, Type type) {
12131213
}
12141214
}
12151215

1216+
/// Check that, if this declaration is a member of an `@_objcImplementation`
1217+
/// extension, it is either `final` or `@objc` (which may have been inferred by
1218+
/// checking whether it shadows an imported declaration).
1219+
static void checkObjCImplementationMemberAvoidsVTable(ValueDecl *VD) {
1220+
auto ED = dyn_cast<ExtensionDecl>(VD->getDeclContext());
1221+
if (!ED || !ED->isObjCImplementation()) return;
1222+
1223+
if (VD->isSemanticallyFinal() || VD->isObjC()) return;
1224+
1225+
auto &diags = VD->getASTContext().Diags;
1226+
diags.diagnose(VD, diag::member_of_objc_implementation_not_objc_or_final,
1227+
VD->getDescriptiveKind(), VD, ED->getExtendedNominal());
1228+
1229+
if (canBeRepresentedInObjC(VD))
1230+
diags.diagnose(VD, diag::fixit_add_objc_for_objc_implementation,
1231+
VD->getDescriptiveKind())
1232+
.fixItInsert(VD->getAttributeInsertionLoc(false), "@objc ");
1233+
1234+
diags.diagnose(VD, diag::fixit_add_final_for_objc_implementation,
1235+
VD->getDescriptiveKind())
1236+
.fixItInsert(VD->getAttributeInsertionLoc(true), "final ");
1237+
}
1238+
12161239
/// Build a default initializer string for the given pattern.
12171240
///
12181241
/// This string is suitable for display in diagnostics.
@@ -1836,6 +1859,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
18361859
(void) VD->isObjC();
18371860
(void) VD->isDynamic();
18381861

1862+
// If this is in an `@_objcImplementation` extension, check whether it's
1863+
// valid there.
1864+
checkObjCImplementationMemberAvoidsVTable(VD);
1865+
18391866
// Check for actor isolation of top-level and local declarations.
18401867
// Declarations inside types are handled in checkConformancesInContext()
18411868
// to avoid cycles involving associated type inference.

test/decl/ext/objc_implementation.swift

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
@_objcImplementation extension ObjCClass {
66
func method(fromHeader1: CInt) {
77
// FIXME: OK, provides an implementation for the header's method.
8+
// expected-error@-2 {{instance method 'method(fromHeader1:)' does not match any instance method declared in the headers for 'ObjCClass'; did you use the instance method's Swift name?}}
9+
// expected-note@-3 {{add '@objc' to define an Objective-C-compatible instance method not declared in the header}} {{3-3=@objc }}
10+
// expected-note@-4 {{add 'final' to define a Swift instance method that cannot be overridden}} {{3-3=final }}
811
}
912

1013
@objc func method(fromHeader2: CInt) {
@@ -13,6 +16,9 @@
1316

1417
func categoryMethod(fromHeader3: CInt) {
1518
// FIXME: Should complain about the wrong category
19+
// expected-error@-2 {{instance method 'categoryMethod(fromHeader3:)' does not match any instance method declared in the headers for 'ObjCClass'; did you use the instance method's Swift name?}}
20+
// expected-note@-3 {{add '@objc' to define an Objective-C-compatible instance method not declared in the header}} {{3-3=@objc }}
21+
// expected-note@-4 {{add 'final' to define a Swift instance method that cannot be overridden}} {{3-3=final }}
1622
}
1723

1824
@objc fileprivate func methodNot(fromHeader1: CInt) {
@@ -24,18 +30,29 @@
2430
}
2531

2632
func methodNot(fromHeader3: CInt) {
27-
// FIXME: Should complain about unmatched, un-attributed method
33+
// expected-error@-1 {{instance method 'methodNot(fromHeader3:)' does not match any instance method declared in the headers for 'ObjCClass'; did you use the instance method's Swift name?}}
34+
// expected-note@-2 {{add '@objc' to define an Objective-C-compatible instance method not declared in the header}} {{3-3=@objc }}
35+
// expected-note@-3 {{add 'final' to define a Swift instance method that cannot be overridden}} {{3-3=final }}
2836
}
2937
}
3038

3139
// FIXME: Should complain about categoryMethodFromHeader4:
3240
@_objcImplementation(PresentAdditions) extension ObjCClass {
3341
func method(fromHeader3: CInt) {
3442
// FIXME: Should complain about wrong category
43+
// expected-error@-2 {{instance method 'method(fromHeader3:)' does not match any instance method declared in the headers for 'ObjCClass'; did you use the instance method's Swift name?}}
44+
// expected-note@-3 {{add '@objc' to define an Objective-C-compatible instance method not declared in the header}} {{3-3=@objc }}
45+
// expected-note@-4 {{add 'final' to define a Swift instance method that cannot be overridden}} {{3-3=final }}
3546
}
3647

3748
func categoryMethod(fromHeader1: CInt) {
3849
// FIXME: OK, provides an implementation for the header's method.
50+
// expected-error@-2 {{instance method 'categoryMethod(fromHeader1:)' does not match any instance method declared in the headers for 'ObjCClass'; did you use the instance method's Swift name?}}
51+
// expected-note@-3 {{add '@objc' to define an Objective-C-compatible instance method not declared in the header}} {{3-3=@objc }}
52+
// expected-note@-4 {{add 'final' to define a Swift instance method that cannot be overridden}} {{3-3=final }}
53+
}
54+
55+
@objc func categoryMethod(fromHeader2: CInt) {
3956
// OK, provides an implementation for the header's method.
4057
}
4158

@@ -48,7 +65,9 @@
4865
}
4966

5067
func categoryMethodNot(fromHeader3: CInt) {
51-
// FIXME: Should complain about unmatched, un-attributed method
68+
// expected-error@-1 {{instance method 'categoryMethodNot(fromHeader3:)' does not match any instance method declared in the headers for 'ObjCClass'; did you use the instance method's Swift name?}}
69+
// expected-note@-2 {{add '@objc' to define an Objective-C-compatible instance method not declared in the header}} {{3-3=@objc }}
70+
// expected-note@-3 {{add 'final' to define a Swift instance method that cannot be overridden}} {{3-3=final }}
5271
}
5372
}
5473

@@ -72,3 +91,23 @@
7291

7392
@_objcImplementation(WTF) extension SwiftClass {} // expected
7493
// 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=}}
94+
95+
func usesAreNotAmbiguous(obj: ObjCClass) {
96+
obj.method(fromHeader1: 1)
97+
obj.method(fromHeader2: 2)
98+
obj.method(fromHeader3: 3)
99+
obj.method(fromHeader4: 4)
100+
101+
obj.methodNot(fromHeader1: 1)
102+
obj.methodNot(fromHeader2: 2)
103+
obj.methodNot(fromHeader3: 3)
104+
105+
obj.categoryMethod(fromHeader1: 1)
106+
obj.categoryMethod(fromHeader2: 2)
107+
obj.categoryMethod(fromHeader3: 3)
108+
obj.categoryMethod(fromHeader4: 4)
109+
110+
obj.categoryMethodNot(fromHeader1: 1)
111+
obj.categoryMethodNot(fromHeader2: 2)
112+
obj.categoryMethodNot(fromHeader3: 3)
113+
}

0 commit comments

Comments
 (0)