Skip to content

Commit 1b7adbe

Browse files
committed
Diagnose invalid Swift-only @objcImpl inits
Required and designated inits have to be overridable—the former so that uses of the initializer on `any T.Type` will call the subclass initializer, the latter so that inherited convenience inits will call the subclass initializer. However, Swift-only members use vtables to dispatch to subclasses, and @objcImpl classes don’t have vtables, so their Swift-only inits cannot be made overridable. Upshot: Swift-only inits on @objcImpl classes must be `convenience`, not `required` or designated. Enforce this rule in the ObjCImplementationChecker. Fixes rdar://109121293.
1 parent 9bb71ff commit 1b7adbe

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1841,6 +1841,20 @@ NOTE(objc_implementation_requirement_here,none,
18411841
"%kind0 declared in header here",
18421842
(ValueDecl *))
18431843

1844+
ERROR(objc_implementation_init_must_be_convenience, none,
1845+
"%kind0 is not valid in an '@_objcImplementation' extension because "
1846+
"Objective-C subclasses must be able to override "
1847+
"%select{designated|required}1 initializers",
1848+
(const ValueDecl *, /*isRequired=*/bool))
1849+
NOTE(objc_implementation_init_turn_designated_to_convenience, none,
1850+
"add 'convenience' keyword to make this a convenience initializer",
1851+
())
1852+
NOTE(objc_implementation_init_turn_required_to_convenience, none,
1853+
"replace 'required' keyword with 'convenience' to make this a convenience "
1854+
"initializer",
1855+
())
1856+
1857+
// Fallback diagnostic; super-general by nature.
18441858
ERROR(objc_implementation_member_requires_vtable, none,
18451859
"%kind0 is not valid in an '@_objcImplementation' extension because it "
18461860
"is an overridable Swift-only %kindonly0",

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2984,6 +2984,28 @@ class ObjCImplementationChecker {
29842984
if (attr->isInvalid())
29852985
return;
29862986

2987+
if (auto init = dyn_cast<ConstructorDecl>(afd)) {
2988+
if (!init->isObjC() && (init->isRequired() ||
2989+
!init->isConvenienceInit())) {
2990+
// Swift-only initializers have to be convenience.
2991+
diagnose(afd, diag::objc_implementation_init_must_be_convenience,
2992+
afd, init->isRequired());
2993+
2994+
// Add appropriate fix-it to 'convenience'.
2995+
if (auto requiredMod = init->getAttrs().getAttribute<RequiredAttr>())
2996+
diagnose(afd,
2997+
diag::objc_implementation_init_turn_required_to_convenience)
2998+
.fixItReplace(requiredMod->getRange(), "convenience");
2999+
else
3000+
diagnose(afd,
3001+
diag::objc_implementation_init_turn_designated_to_convenience)
3002+
.fixItInsert(afd->getAttributeInsertionLoc(/*forModifier=*/true),
3003+
"convenience ");
3004+
3005+
return;
3006+
}
3007+
}
3008+
29873009
// Emit a vague fallback diagnostic.
29883010
diagnose(afd, diag::objc_implementation_member_requires_vtable, afd);
29893011
}

test/decl/ext/objc_implementation.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,13 @@ protocol EmptySwiftProto {}
203203
}
204204

205205
@nonobjc public init(notFromHeader4: CInt) {
206-
// expected-warning@-1 {{initializer 'init(notFromHeader4:)' is not valid in an '@_objcImplementation' extension because it is an overridable Swift-only initializer}}
206+
// expected-warning@-1 {{initializer 'init(notFromHeader4:)' is not valid in an '@_objcImplementation' extension because Objective-C subclasses must be able to override designated initializers}}
207+
// expected-note@-2 {{add 'convenience' keyword to make this a convenience initializer}} {{12-12=convenience }}
207208
}
208209

209210
@nonobjc public required init(notFromHeader5: CInt) {
210-
// expected-warning@-1 {{initializer 'init(notFromHeader5:)' is not valid in an '@_objcImplementation' extension because it is an overridable Swift-only initializer}}
211+
// expected-warning@-1 {{initializer 'init(notFromHeader5:)' is not valid in an '@_objcImplementation' extension because Objective-C subclasses must be able to override required initializers}}
212+
// expected-note@-2 {{replace 'required' keyword with 'convenience' to make this a convenience initializer}} {{19-27=convenience}}
211213
}
212214

213215
@nonobjc public convenience init(notFromHeader6: CInt) {

0 commit comments

Comments
 (0)