Skip to content

[4.1] [PrintAsObjC] Reintroduce +new when reintroducing -init #13331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 38 additions & 6 deletions lib/PrintAsObjC/PrintAsObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,12 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
return M.getASTContext().TheEmptyTupleType;
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,
bool isClassMethod,
Expand Down Expand Up @@ -605,6 +611,7 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,

bool skipAvailability = false;
bool makeNewUnavailable = false;
bool makeNewExplicitlyAvailable = false;
// Swift designated initializers are Objective-C designated initializers.
if (auto ctor = dyn_cast<ConstructorDecl>(AFD)) {
if (ctor->hasStubImplementation()
Expand All @@ -614,12 +621,34 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
os << " SWIFT_UNAVAILABLE";
skipAvailability = true;
// If -init is unavailable, then +new should be, too:
const bool selectorIsInit = selector.getNumArgs() == 0 && selectorPieces.front().str() == "init";
makeNewUnavailable = selectorIsInit;
} else if (ctor->isDesignatedInit() &&
!isa<ProtocolDecl>(ctor->getDeclContext())) {
os << " OBJC_DESIGNATED_INITIALIZER";
makeNewUnavailable = selectorIsInit(selector);
} else {
if (ctor->isDesignatedInit() &&
!isa<ProtocolDecl>(ctor->getDeclContext())) {
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())) {
os << " SWIFT_METHOD_FAMILY(init)";
}
Expand Down Expand Up @@ -649,7 +678,10 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
os << ";\n";

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";
}
}

Expand Down
35 changes: 35 additions & 0 deletions test/PrintAsObjC/classes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,41 @@ class MyObject : NSObject {}
@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 :
private class Private : A1 {}

Expand Down
8 changes: 8 additions & 0 deletions validation-test/PrintAsObjC/Inputs/reintroduced-new.swift
Original file line number Diff line number Diff line change
@@ -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 changes: 26 additions & 0 deletions validation-test/PrintAsObjC/reintroduced-new.m
Original file line number Diff line number Diff line change
@@ -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: