Skip to content

Commit b14c00d

Browse files
committed
Support required inits in @objcImpl
• Allow `required init`s in @objcImpl extensions of a class’s main body • Validate that the presence or absence of a `required` modifier matches the imported header declaration. Fixes rdar://110016760.
1 parent 9124c0e commit b14c00d

File tree

6 files changed

+70
-2
lines changed

6 files changed

+70
-2
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,6 +1663,11 @@ ERROR(objc_implementation_type_mismatch,none,
16631663
"header",
16641664
(DescriptiveDeclKind, ValueDecl *, Type, Type))
16651665

1666+
ERROR(objc_implementation_required_attr_mismatch,none,
1667+
"%0 %1 %select{should not|should}2 be 'required' to match %0 declared by "
1668+
"the header",
1669+
(DescriptiveDeclKind, ValueDecl *, bool))
1670+
16661671
ERROR(objc_implementation_wrong_objc_name,none,
16671672
"selector %0 for %1 %2 not found in header; did you mean %3?",
16681673
(ObjCSelector, DescriptiveDeclKind, ValueDecl *, ObjCSelector))

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2717,7 +2717,7 @@ void AttributeChecker::visitRequiredAttr(RequiredAttr *attr) {
27172717
// The constructor must be declared within the class itself.
27182718
// FIXME: Allow an SDK overlay to add a required initializer to a class
27192719
// defined in Objective-C
2720-
if (!isa<ClassDecl>(ctor->getDeclContext()) &&
2720+
if (!isa<ClassDecl>(ctor->getDeclContext()->getImplementedObjCContext()) &&
27212721
!isObjCClassExtensionInOverlay(ctor->getDeclContext())) {
27222722
diagnose(ctor, diag::required_initializer_in_extension, parentTy)
27232723
.highlight(attr->getLocation());

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3015,6 +3015,7 @@ class ObjCImplementationChecker {
30153015
WrongDeclKind,
30163016
WrongType,
30173017
WrongWritability,
3018+
WrongRequiredAttr,
30183019

30193020
Match,
30203021
MatchWithExplicitObjCName,
@@ -3312,6 +3313,10 @@ class ObjCImplementationChecker {
33123313
!cast<AbstractStorageDecl>(cand)->isSettable(nullptr))
33133314
return MatchOutcome::WrongWritability;
33143315

3316+
if (auto reqCtor = dyn_cast<ConstructorDecl>(req))
3317+
if (reqCtor->isRequired() != cast<ConstructorDecl>(cand)->isRequired())
3318+
return MatchOutcome::WrongRequiredAttr;
3319+
33153320
// If we got here, everything matched. But at what quality?
33163321
if (explicitObjCName)
33173322
return MatchOutcome::MatchWithExplicitObjCName;
@@ -3399,6 +3404,22 @@ class ObjCImplementationChecker {
33993404
diagnose(cand, diag::objc_implementation_must_be_settable,
34003405
cand->getDescriptiveKind(), cand, req->getDescriptiveKind());
34013406
return;
3407+
3408+
case MatchOutcome::WrongRequiredAttr: {
3409+
bool shouldBeRequired = cast<ConstructorDecl>(req)->isRequired();
3410+
3411+
auto diag =
3412+
diagnose(cand, diag::objc_implementation_required_attr_mismatch,
3413+
cand->getDescriptiveKind(), cand, shouldBeRequired);
3414+
3415+
if (shouldBeRequired)
3416+
diag.fixItInsert(cand->getAttributeInsertionLoc(/*forModifier=*/true),
3417+
"required ");
3418+
else
3419+
diag.fixItRemove(cand->getAttrs().getAttribute<RequiredAttr>()
3420+
->getLocation());
3421+
return;
3422+
}
34023423
}
34033424

34043425
llvm_unreachable("Unknown MatchOutcome");

test/decl/ext/Inputs/objc_implementation.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,16 @@
1212

1313
@end
1414

15-
@interface ObjCClass : ObjCBaseClass
15+
@protocol ObjCProto
16+
17+
- (instancetype)initFromProtocol1:(int)param;
18+
- (instancetype)initFromProtocol2:(int)param;
19+
20+
@end
21+
22+
@interface ObjCClass : ObjCBaseClass <ObjCProto>
23+
24+
- (instancetype)initNotFromProtocol:(int)param;
1625

1726
- (void)methodFromHeader1:(int)param;
1827
- (void)methodFromHeader2:(int)param;

test/decl/ext/objc_implementation.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,21 @@
145145
// OK
146146
}
147147

148+
@objc(initFromProtocol1:)
149+
required public init?(fromProtocol1: CInt) {
150+
// OK
151+
}
152+
153+
@objc(initFromProtocol2:)
154+
public init?(fromProtocol2: CInt) {
155+
// expected-error@-1 {{initializer 'init(fromProtocol2:)' should be 'required' to match initializer declared by the header}} {{3-3=required }}
156+
}
157+
158+
@objc(initNotFromProtocol:)
159+
required public init?(notFromProtocol: CInt) {
160+
// expected-error@-1 {{initializer 'init(notFromProtocol:)' should not be 'required' to match initializer declared by the header}} {{3-12=}}
161+
}
162+
148163
class func classMethod1(_: CInt) {
149164
// OK
150165
}

test/decl/ext/objc_implementation_conflicts.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,24 @@ import objc_implementation_private
146146
super.init(fromSuperclass2: v)
147147
}
148148

149+
@objc(initFromProtocol1:)
150+
required public init?(fromProtocol1 v: CInt) {
151+
// OK
152+
super.init(fromSuperclass: v)
153+
}
154+
155+
@objc(initFromProtocol2:)
156+
required public init?(fromProtocol2 v: CInt) {
157+
// OK
158+
super.init(fromSuperclass: v)
159+
}
160+
161+
@objc(initNotFromProtocol:)
162+
public init?(notFromProtocol v: CInt) {
163+
// OK
164+
super.init(fromSuperclass: v)
165+
}
166+
149167
class func classMethod1(_: CInt) {}
150168
class func classMethod2(_: CInt) {}
151169
class func classMethod3(_: CInt) {}

0 commit comments

Comments
 (0)