mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[PrintAsObjC] Reintroduce +new when reintroducing -init. (#13320)
Follow-up to 49c65facc9 to account for subclasses. An unavailable
-init makes +new unavailable, but reintroducing it in a subclass
should reintroduce +new if the root class is NSObject (where +new is
implemented).
rdar://problem/35914080
This commit is contained in:
@@ -501,6 +501,12 @@ private:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if \p sel is the no-argument selector 'init'.
|
||||||
|
static bool selectorIsInit(ObjCSelector sel) {
|
||||||
|
return sel.getNumArgs() == 0 &&
|
||||||
|
sel.getSelectorPieces().front().str() == "init";
|
||||||
|
}
|
||||||
|
|
||||||
void printAbstractFunctionAsMethod(AbstractFunctionDecl *AFD,
|
void printAbstractFunctionAsMethod(AbstractFunctionDecl *AFD,
|
||||||
bool isClassMethod,
|
bool isClassMethod,
|
||||||
bool isNSUIntegerSubscript = false) {
|
bool isNSUIntegerSubscript = false) {
|
||||||
@@ -605,6 +611,7 @@ private:
|
|||||||
|
|
||||||
bool skipAvailability = false;
|
bool skipAvailability = false;
|
||||||
bool makeNewUnavailable = false;
|
bool makeNewUnavailable = false;
|
||||||
|
bool makeNewExplicitlyAvailable = false;
|
||||||
// Swift designated initializers are Objective-C designated initializers.
|
// Swift designated initializers are Objective-C designated initializers.
|
||||||
if (auto ctor = dyn_cast<ConstructorDecl>(AFD)) {
|
if (auto ctor = dyn_cast<ConstructorDecl>(AFD)) {
|
||||||
if (ctor->hasStubImplementation()
|
if (ctor->hasStubImplementation()
|
||||||
@@ -614,12 +621,34 @@ private:
|
|||||||
os << " SWIFT_UNAVAILABLE";
|
os << " SWIFT_UNAVAILABLE";
|
||||||
skipAvailability = true;
|
skipAvailability = true;
|
||||||
// If -init is unavailable, then +new should be, too:
|
// If -init is unavailable, then +new should be, too:
|
||||||
const bool selectorIsInit = selector.getNumArgs() == 0 && selectorPieces.front().str() == "init";
|
makeNewUnavailable = selectorIsInit(selector);
|
||||||
makeNewUnavailable = selectorIsInit;
|
} else {
|
||||||
} else if (ctor->isDesignatedInit() &&
|
if (ctor->isDesignatedInit() &&
|
||||||
!isa<ProtocolDecl>(ctor->getDeclContext())) {
|
!isa<ProtocolDecl>(ctor->getDeclContext())) {
|
||||||
os << " OBJC_DESIGNATED_INITIALIZER";
|
os << " OBJC_DESIGNATED_INITIALIZER";
|
||||||
|
}
|
||||||
|
|
||||||
|
// If -init is newly available, +new should be as well if the class
|
||||||
|
// inherits from NSObject.
|
||||||
|
if (selectorIsInit(selector) && !ctor->getOverriddenDecl()) {
|
||||||
|
auto container = ctor->getDeclContext();
|
||||||
|
auto *classDecl = container->getAsClassOrClassExtensionContext();
|
||||||
|
if (!classDecl) {
|
||||||
|
assert(container->getAsProtocolOrProtocolExtensionContext());
|
||||||
|
} else {
|
||||||
|
while (classDecl->hasSuperclass()) {
|
||||||
|
classDecl = classDecl->getSuperclassDecl();
|
||||||
|
assert(classDecl &&
|
||||||
|
"shouldn't PrintAsObjC with invalid superclasses");
|
||||||
|
}
|
||||||
|
if (classDecl->hasClangNode() &&
|
||||||
|
classDecl->getNameStr() == "NSObject") {
|
||||||
|
makeNewExplicitlyAvailable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!looksLikeInitMethod(AFD->getObjCSelector())) {
|
if (!looksLikeInitMethod(AFD->getObjCSelector())) {
|
||||||
os << " SWIFT_METHOD_FAMILY(init)";
|
os << " SWIFT_METHOD_FAMILY(init)";
|
||||||
}
|
}
|
||||||
@@ -649,7 +678,10 @@ private:
|
|||||||
os << ";\n";
|
os << ";\n";
|
||||||
|
|
||||||
if (makeNewUnavailable) {
|
if (makeNewUnavailable) {
|
||||||
os << "+ (nonnull instancetype)new SWIFT_UNAVAILABLE;\n";
|
assert(!makeNewExplicitlyAvailable);
|
||||||
|
os << "+ (nonnull instancetype)new SWIFT_UNAVAILABLE;\n";
|
||||||
|
} else if (makeNewExplicitlyAvailable) {
|
||||||
|
os << "+ (nonnull instancetype)new;\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -433,6 +433,41 @@ class MyObject : NSObject {}
|
|||||||
@objc class Subclass : NestedSuperclass {}
|
@objc class Subclass : NestedSuperclass {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface NewBanned
|
||||||
|
// CHECK-NEXT: - (nonnull instancetype)initWithArbitraryArgument:(NSInteger)arbitraryArgument OBJC_DESIGNATED_INITIALIZER;
|
||||||
|
// CHECK-NEXT: - (nonnull instancetype)init SWIFT_UNAVAILABLE;
|
||||||
|
// CHECK-NEXT: + (nonnull instancetype)new SWIFT_UNAVAILABLE;
|
||||||
|
// CHECK-NEXT: @end
|
||||||
|
@objc class NewBanned : NSObject {
|
||||||
|
init(arbitraryArgument: Int) { super.init() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface NewBanned
|
||||||
|
// CHECK-NEXT: - (nonnull instancetype)initWithDifferentArbitraryArgument:(NSInteger)differentArbitraryArgument OBJC_DESIGNATED_INITIALIZER;
|
||||||
|
// CHECK-NEXT: - (nonnull instancetype)initWithArbitraryArgument:(NSInteger)arbitraryArgument SWIFT_UNAVAILABLE;
|
||||||
|
// CHECK-NEXT: @end
|
||||||
|
@objc class NewBannedStill : NewBanned {
|
||||||
|
init(differentArbitraryArgument: Int) { super.init(arbitraryArgument: 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface NewUnbanned
|
||||||
|
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
|
||||||
|
// CHECK-NEXT: + (nonnull instancetype)new;
|
||||||
|
// CHECK-NEXT: - (nonnull instancetype)initWithArbitraryArgument:(NSInteger)arbitraryArgument SWIFT_UNAVAILABLE;
|
||||||
|
// CHECK-NEXT: @end
|
||||||
|
@objc class NewUnbanned : NewBanned {
|
||||||
|
init() { super.init(arbitraryArgument: 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface NewUnbannedDouble
|
||||||
|
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
|
||||||
|
// CHECK-NEXT: + (nonnull instancetype)new;
|
||||||
|
// CHECK-NEXT: - (nonnull instancetype)initWithDifferentArbitraryArgument:(NSInteger)differentArbitraryArgument SWIFT_UNAVAILABLE;
|
||||||
|
// CHECK-NEXT: @end
|
||||||
|
@objc class NewUnbannedDouble : NewBannedStill {
|
||||||
|
init() { super.init(differentArbitraryArgument: 0) }
|
||||||
|
}
|
||||||
|
|
||||||
// NEGATIVE-NOT: @interface Private :
|
// NEGATIVE-NOT: @interface Private :
|
||||||
private class Private : A1 {}
|
private class Private : A1 {}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import ObjectiveC
|
||||||
|
|
||||||
|
public class Base : NSObject {
|
||||||
|
public init(foo: Int) { super.init() }
|
||||||
|
}
|
||||||
|
public class Sub: Base {
|
||||||
|
@objc public init() { super.init(foo: 0) }
|
||||||
|
}
|
||||||
26
validation-test/PrintAsObjC/reintroduced-new.m
Normal file
26
validation-test/PrintAsObjC/reintroduced-new.m
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// REQUIRES: objc_interop
|
||||||
|
|
||||||
|
// RUN: %empty-directory(%t)
|
||||||
|
|
||||||
|
// FIXME: BEGIN -enable-source-import hackaround
|
||||||
|
// 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
|
||||||
|
// FIXME: END -enable-source-import hackaround
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// 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
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// CHECK-NOT: error:
|
||||||
|
|
||||||
|
void test() {
|
||||||
|
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'init' is unavailable
|
||||||
|
(void)[[Base alloc] init];
|
||||||
|
// CHECK-NOT: error:
|
||||||
|
(void)[[Sub alloc] init];
|
||||||
|
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'new' is unavailable
|
||||||
|
(void)[Base new];
|
||||||
|
// CHECK-NOT: error:
|
||||||
|
(void)[Sub new];
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-NOT: error:
|
||||||
Reference in New Issue
Block a user