Skip to content

Commit 54e3200

Browse files
author
ematejska
authored
Merge pull request #13331 from jrose-apple/4.1-new-and-improved
[4.1] [PrintAsObjC] Reintroduce +new when reintroducing -init
2 parents 4c99be4 + 7c789dc commit 54e3200

File tree

4 files changed

+107
-6
lines changed

4 files changed

+107
-6
lines changed

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,12 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
500500
return M.getASTContext().TheEmptyTupleType;
501501
return result;
502502
}
503+
504+
/// Returns true if \p sel is the no-argument selector 'init'.
505+
static bool selectorIsInit(ObjCSelector sel) {
506+
return sel.getNumArgs() == 0 &&
507+
sel.getSelectorPieces().front().str() == "init";
508+
}
503509

504510
void printAbstractFunctionAsMethod(AbstractFunctionDecl *AFD,
505511
bool isClassMethod,
@@ -605,6 +611,7 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
605611

606612
bool skipAvailability = false;
607613
bool makeNewUnavailable = false;
614+
bool makeNewExplicitlyAvailable = false;
608615
// Swift designated initializers are Objective-C designated initializers.
609616
if (auto ctor = dyn_cast<ConstructorDecl>(AFD)) {
610617
if (ctor->hasStubImplementation()
@@ -614,12 +621,34 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
614621
os << " SWIFT_UNAVAILABLE";
615622
skipAvailability = true;
616623
// If -init is unavailable, then +new should be, too:
617-
const bool selectorIsInit = selector.getNumArgs() == 0 && selectorPieces.front().str() == "init";
618-
makeNewUnavailable = selectorIsInit;
619-
} else if (ctor->isDesignatedInit() &&
620-
!isa<ProtocolDecl>(ctor->getDeclContext())) {
621-
os << " OBJC_DESIGNATED_INITIALIZER";
624+
makeNewUnavailable = selectorIsInit(selector);
625+
} else {
626+
if (ctor->isDesignatedInit() &&
627+
!isa<ProtocolDecl>(ctor->getDeclContext())) {
628+
os << " OBJC_DESIGNATED_INITIALIZER";
629+
}
630+
631+
// If -init is newly available, +new should be as well if the class
632+
// inherits from NSObject.
633+
if (selectorIsInit(selector) && !ctor->getOverriddenDecl()) {
634+
auto container = ctor->getDeclContext();
635+
auto *classDecl = container->getAsClassOrClassExtensionContext();
636+
if (!classDecl) {
637+
assert(container->getAsProtocolOrProtocolExtensionContext());
638+
} else {
639+
while (classDecl->hasSuperclass()) {
640+
classDecl = classDecl->getSuperclassDecl();
641+
assert(classDecl &&
642+
"shouldn't PrintAsObjC with invalid superclasses");
643+
}
644+
if (classDecl->hasClangNode() &&
645+
classDecl->getNameStr() == "NSObject") {
646+
makeNewExplicitlyAvailable = true;
647+
}
648+
}
649+
}
622650
}
651+
623652
if (!looksLikeInitMethod(AFD->getObjCSelector())) {
624653
os << " SWIFT_METHOD_FAMILY(init)";
625654
}
@@ -649,7 +678,10 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
649678
os << ";\n";
650679

651680
if (makeNewUnavailable) {
652-
os << "+ (nonnull instancetype)new SWIFT_UNAVAILABLE;\n";
681+
assert(!makeNewExplicitlyAvailable);
682+
os << "+ (nonnull instancetype)new SWIFT_UNAVAILABLE;\n";
683+
} else if (makeNewExplicitlyAvailable) {
684+
os << "+ (nonnull instancetype)new;\n";
653685
}
654686
}
655687

test/PrintAsObjC/classes.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,41 @@ class MyObject : NSObject {}
433433
@objc class Subclass : NestedSuperclass {}
434434
}
435435

436+
// CHECK-LABEL: @interface NewBanned
437+
// CHECK-NEXT: - (nonnull instancetype)initWithArbitraryArgument:(NSInteger)arbitraryArgument OBJC_DESIGNATED_INITIALIZER;
438+
// CHECK-NEXT: - (nonnull instancetype)init SWIFT_UNAVAILABLE;
439+
// CHECK-NEXT: + (nonnull instancetype)new SWIFT_UNAVAILABLE;
440+
// CHECK-NEXT: @end
441+
@objc class NewBanned : NSObject {
442+
init(arbitraryArgument: Int) { super.init() }
443+
}
444+
445+
// CHECK-LABEL: @interface NewBanned
446+
// CHECK-NEXT: - (nonnull instancetype)initWithDifferentArbitraryArgument:(NSInteger)differentArbitraryArgument OBJC_DESIGNATED_INITIALIZER;
447+
// CHECK-NEXT: - (nonnull instancetype)initWithArbitraryArgument:(NSInteger)arbitraryArgument SWIFT_UNAVAILABLE;
448+
// CHECK-NEXT: @end
449+
@objc class NewBannedStill : NewBanned {
450+
init(differentArbitraryArgument: Int) { super.init(arbitraryArgument: 0) }
451+
}
452+
453+
// CHECK-LABEL: @interface NewUnbanned
454+
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
455+
// CHECK-NEXT: + (nonnull instancetype)new;
456+
// CHECK-NEXT: - (nonnull instancetype)initWithArbitraryArgument:(NSInteger)arbitraryArgument SWIFT_UNAVAILABLE;
457+
// CHECK-NEXT: @end
458+
@objc class NewUnbanned : NewBanned {
459+
init() { super.init(arbitraryArgument: 0) }
460+
}
461+
462+
// CHECK-LABEL: @interface NewUnbannedDouble
463+
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
464+
// CHECK-NEXT: + (nonnull instancetype)new;
465+
// CHECK-NEXT: - (nonnull instancetype)initWithDifferentArbitraryArgument:(NSInteger)differentArbitraryArgument SWIFT_UNAVAILABLE;
466+
// CHECK-NEXT: @end
467+
@objc class NewUnbannedDouble : NewBannedStill {
468+
init() { super.init(differentArbitraryArgument: 0) }
469+
}
470+
436471
// NEGATIVE-NOT: @interface Private :
437472
private class Private : A1 {}
438473

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import ObjectiveC
2+
3+
public class Base : NSObject {
4+
public init(foo: Int) { super.init() }
5+
}
6+
public class Sub: Base {
7+
@objc public init() { super.init(foo: 0) }
8+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// REQUIRES: objc_interop
2+
3+
// RUN: %empty-directory(%t)
4+
5+
// FIXME: BEGIN -enable-source-import hackaround
6+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../../test/Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../../test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
7+
// FIXME: END -enable-source-import hackaround
8+
9+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../../test/Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/Inputs/reintroduced-new.swift -disable-objc-attr-requires-foundation-module -module-name main
10+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../../test/Inputs/clang-importer-sdk -I %t) -parse-as-library %t/main.swiftmodule -typecheck -emit-objc-header-path %t/generated.h -disable-objc-attr-requires-foundation-module
11+
// RUN: not %clang -fsyntax-only -x objective-c %s -include %t/generated.h -fobjc-arc -fmodules -Werror -isysroot %S/../../test/Inputs/clang-importer-sdk 2>&1 | %FileCheck %s
12+
13+
// CHECK-NOT: error:
14+
15+
void test() {
16+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'init' is unavailable
17+
(void)[[Base alloc] init];
18+
// CHECK-NOT: error:
19+
(void)[[Sub alloc] init];
20+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'new' is unavailable
21+
(void)[Base new];
22+
// CHECK-NOT: error:
23+
(void)[Sub new];
24+
}
25+
26+
// CHECK-NOT: error:

0 commit comments

Comments
 (0)