Skip to content
Closed
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
1 change: 1 addition & 0 deletions lib/ClangImporter/ClangAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ OmissionTypeName importer::getClangTypeNameForOmission(clang::ASTContext &ctx,

// For id<Proto> or NSObject<Proto>, retrieve the name of "Proto".
if (objcObjectPtr->getNumProtocols() == 1 &&
!isNSObjectProtocol(*objcObjectPtr->qual_begin()) &&
(!objcClass || objcClass->getName() == "NSObject"))
return (*objcObjectPtr->qual_begin())->getName();

Expand Down
12 changes: 12 additions & 0 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6714,6 +6714,8 @@ void SwiftDeclConverter::importObjCProtocols(

for (auto cp = clangProtocols.begin(), cpEnd = clangProtocols.end();
cp != cpEnd; ++cp) {
if (isNSObjectProtocol(*cp)) continue;

if (auto proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
Impl.importDecl(*cp, getActiveSwiftVersion()))) {
addProtocols(proto, protocols, knownProtocols);
Expand Down Expand Up @@ -6797,6 +6799,8 @@ Optional<GenericParamList *> SwiftDeclConverter::importObjCGenericParams(
inherited.push_back(TypeLoc::withoutLoc(superclassType));
}
for (clang::ObjCProtocolDecl *clangProto : clangBound->quals()) {
if (isNSObjectProtocol(clangProto)) continue;

ProtocolDecl *proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
Impl.importDecl(clangProto, getActiveSwiftVersion()));
if (!proto) {
Expand Down Expand Up @@ -8555,6 +8559,14 @@ void ClangImporter::Implementation::collectMembersToAdd(
members);
}

// For NSObject, add the otherwise-hidden NSObject protocol. This is only
// used to import mirrored members.
// FIXME: Check for any class that conforms to the NSObject protocol?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe NSProxy is the only other one in the Apple SDKs. We are absolutely terrible with it but if you wanted to avoid touching it maybe you could make the condition about root classes instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The principled thing would be to check whether it's a root class that adopts the protocol in Objective-C.

if (clangClass->getName() == "NSObject") {
if (auto nsObjectProto = getNSObjectProtocolType()) {
protos.push_back(nsObjectProto->castTo<ProtocolType>()->getDecl());
}
}
} else if (auto clangProto
= dyn_cast<clang::ObjCProtocolDecl>(objcContainer)) {
objcContainer = clangProto->getDefinition();
Expand Down
7 changes: 7 additions & 0 deletions lib/ClangImporter/ImportName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,9 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
if (version == ImportNameVersion::raw())
return nullptr;

// HACK: Don't rename the NSObject protocol.
if (isNSObjectProtocol(decl)) return nullptr;

// Handle versioned API notes for Swift 3 and later. This is the common case.
if (version > ImportNameVersion::swift2()) {
const auto *activeAttr = decl->getAttr<clang::SwiftNameAttr>();
Expand Down Expand Up @@ -1021,6 +1024,10 @@ static bool shouldBeSwiftPrivate(NameImporter &nameImporter,
}
}

// The NSObject protocol is SwiftPrivate.
// FIXME: Move this into the API notes for the ObjectiveC module.
if (isNSObjectProtocol(decl)) return true;

return false;
}

Expand Down
27 changes: 15 additions & 12 deletions lib/ClangImporter/ImportType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -926,17 +926,6 @@ namespace {
SmallVector<clang::ObjCProtocolDecl *, 4> protocols{
type->qual_begin(), type->qual_end()
};
auto *nsObjectProto =
Impl.getNSObjectProtocolType()->getAnyNominal();
if (!nsObjectProto) {
// Input is malformed
return {};
}
auto *clangProto =
cast<clang::ObjCProtocolDecl>(nsObjectProto->getClangDecl());
protocols.push_back(
const_cast<clang::ObjCProtocolDecl *>(clangProto));

clang::ASTContext &clangCtx = Impl.getClangASTContext();
clang::QualType protosOnlyType =
clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy,
Expand Down Expand Up @@ -1037,8 +1026,15 @@ namespace {
if (!importedType->isAnyObject())
members.push_back(importedType);

bool hasNSObjectProtocol = false;
for (auto cp = type->qual_begin(), cpEnd = type->qual_end();
cp != cpEnd; ++cp) {
// Never import the NSObject protocol in a type.
if (isNSObjectProtocol(*cp)) {
hasNSObjectProtocol = true;
continue;
}

auto proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
Impl.importDecl(*cp, Impl.CurrentVersion));
if (!proto)
Expand All @@ -1049,7 +1045,7 @@ namespace {

importedType = ProtocolCompositionType::get(Impl.SwiftContext,
members,
/*HasExplicitAnyObject=*/false);
/*HasExplicitAnyObject=*/hasNSObjectProtocol);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem so important when members is known to contain at least one @objc protocol. Or does this handle the 0 case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only really relevant for the 0 case; I can gate it on that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, this is the part that forces us to import such things as AnyObject rather than Any; @jckarter was arguing that we should import as Any.

}

// Class or Class<P> maps to an existential metatype.
Expand Down Expand Up @@ -2473,3 +2469,10 @@ Type ClangImporter::Implementation::getNSCopyingType() {
Type ClangImporter::Implementation::getNSObjectProtocolType() {
return getNamedProtocolType(*this, "NSObject");
}

bool swift::importer::isNSObjectProtocol(const clang::Decl *decl) {
if (auto objcProto = dyn_cast_or_null<clang::ObjCProtocolDecl>(decl))
return objcProto->getName() == "NSObject";
return false;
}

2 changes: 2 additions & 0 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,8 @@ static T *castIgnoringCompatibilityAlias(Decl *D) {
return cast_or_null<T>(D);
}

bool isNSObjectProtocol(const clang::Decl *decl);

class SwiftNameLookupExtension : public clang::ModuleFileExtension {
std::unique_ptr<SwiftLookupTable> &pchLookupTable;
LookupTableMap &lookupTables;
Expand Down
17 changes: 15 additions & 2 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,18 @@ void TypeChecker::resolveInheritanceClause(
}
}

/// Determine whether this type refers to the deprecated NSObjectProtocol
/// type alias.
static bool isDeprecatedNSObjectProtocol(Type type) {
if (auto typeAlias = dyn_cast<NameAliasType>(type.getPointer())) {
if (typeAlias->getDecl()->getAttrs().getDeprecated(type->getASTContext()) &&
typeAlias->getDecl()->getName().str() == "NSObjectProtocol")
return true;
}

return false;
}

/// check the inheritance clause of a type declaration or extension thereof.
///
/// This routine validates all of the types in the parsed inheritance clause,
Expand Down Expand Up @@ -518,8 +530,9 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
}

// Swift 3 compatibility -- a class inheriting from AnyObject is a no-op.
if (Context.LangOpts.isSwiftVersion3() && isa<ClassDecl>(decl) &&
inheritedTy->isAnyObject()) {
if (isa<ClassDecl>(decl) && inheritedTy->isAnyObject() &&
(Context.LangOpts.isSwiftVersion3() ||
isDeprecatedNSObjectProtocol(inheritedTy))) {
auto classDecl = cast<ClassDecl>(decl);
auto removeRange = getRemovalRange(i);
diagnose(inherited.getSourceRange().Start,
Expand Down
5 changes: 4 additions & 1 deletion stdlib/public/SDK/ObjectiveC/ObjectiveC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,10 @@ public var NO: ObjCBool {

// NSObject implements Equatable's == as -[NSObject isEqual:]
// NSObject implements Hashable's hashValue() as -[NSObject hash]
// FIXME: what about NSObjectProtocol?

@available(*, deprecated, renamed: "AnyObject",
message: "all classes implicitly conform to the 'NSObject' protocol")
public typealias NSObjectProtocol = AnyObject

extension NSObject : Equatable, Hashable {
/// The hash value.
Expand Down
4 changes: 2 additions & 2 deletions test/ClangImporter/Dispatch_test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import Foundation

func test1(_ queue: dispatch_queue_t) {} // expected-error {{'dispatch_queue_t' is unavailable}}
func test2(_ queue: DispatchQueue) {
let base: NSObjectProtocol = queue
let base: AnyObject = queue
let _: DispatchObject = queue

let _ = base as? DispatchQueue

// Make sure the dispatch types are actually distinct types!
let _ = queue as DispatchSource // expected-error {{cannot convert value of type 'DispatchQueue' to type 'DispatchSource' in coercion}}
let _ = base as DispatchSource // expected-error {{'NSObjectProtocol' is not convertible to 'DispatchSource'; did you mean to use 'as!' to force downcast?}}
let _ = base as DispatchSource // expected-error {{'AnyObject' is not convertible to 'DispatchSource'; did you mean to use 'as!' to force downcast?}}
}

extension dispatch_queue_t {} // expected-error {{'dispatch_queue_t' is unavailable}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func test(_ foo : FooProto) {
@objc class ForwardClass : NSObject {
}

@objc protocol ForwardProto : NSObjectProtocol {
@objc protocol ForwardProto : AnyObject {
}
@objc class ForwardProtoAdopter : NSObject, ForwardProto {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@objc class ForwardClass : NSObject {
}

@objc protocol ForwardProto : NSObjectProtocol {
@objc protocol ForwardProto : AnyObject {
}
@objc class ForwardProtoAdopter : NSObject, ForwardProto {
}
Expand Down
4 changes: 0 additions & 4 deletions test/ClangImporter/availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ func test_unavailable_instance_method(_ x : NSObject) -> Bool {
return x.allowsWeakReference() // expected-error {{'allowsWeakReference()' is unavailable}}
}

func test_unavailable_method_in_protocol(_ x : NSObjectProtocol) {
// expected-warning @+1 {{expression of type 'NSObjectProtocol' is unused}}
x.retain() // expected-error {{'retain()' is unavailable}}
}
func test_unavailable_method_in_protocol_use_class_instance(_ x : NSObject) {
x.retain() // expected-error {{'retain()' is unavailable}} expected-warning {{result of call to 'retain()' is unused}}
}
Expand Down
18 changes: 8 additions & 10 deletions test/ClangImporter/objc_parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func instanceMethods(_ b: B) {

// Instance methods with keyword components
var obj = NSObject()
var prot = NSObjectProtocol.self
var prot = NSCoding.self
b.`protocol`(prot, hasThing:obj)
b.doThing(obj, protocol: prot)
}
Expand Down Expand Up @@ -311,10 +311,10 @@ func ivars(_ hive: Hive) {
hive.queen.description() // expected-error{{value of type 'Hive' has no member 'queen'}}
}

class NSObjectable : NSObjectProtocol {
@objc var description : String { return "" }
@objc(conformsToProtocol:) func conforms(to _: Protocol) -> Bool { return false }
@objc(isKindOfClass:) func isKind(of aClass: AnyClass) -> Bool { return false }
class NSObjectable : NSObject {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be more interesting if this didn't subclass anything, but it probably doesn't really matter at this point.

@objc override var description : String { return "" }
@objc(conformsToProtocol:) override func conforms(to _: Protocol) -> Bool { return false }
@objc(isKindOfClass:) override func isKind(of aClass: AnyClass) -> Bool { return false }
}


Expand Down Expand Up @@ -553,17 +553,15 @@ func testStrangeSelectors(obj: StrangeSelectors) {

func testProtocolQualified(_ obj: CopyableNSObject, cell: CopyableSomeCell,
plainObj: NSObject, plainCell: SomeCell) {
_ = obj as NSObject // expected-error {{'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol') is not convertible to 'NSObject'; did you mean to use 'as!' to force downcast?}} {{11-13=as!}}
_ = obj as NSObjectProtocol
_ = obj as NSObject // expected-error {{'CopyableNSObject' (aka 'NSCopying') is not convertible to 'NSObject'; did you mean to use 'as!' to force downcast?}} {{11-13=as!}}
_ = obj as NSCopying
_ = obj as SomeCell // expected-error {{'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol') is not convertible to 'SomeCell'; did you mean to use 'as!' to force downcast?}} {{11-13=as!}}
_ = obj as SomeCell // expected-error {{'CopyableNSObject' (aka 'NSCopying') is not convertible to 'SomeCell'; did you mean to use 'as!' to force downcast?}} {{11-13=as!}}

_ = cell as NSObject
_ = cell as NSObjectProtocol
_ = cell as NSCopying // expected-error {{'CopyableSomeCell' (aka 'SomeCell') is not convertible to 'NSCopying'; did you mean to use 'as!' to force downcast?}} {{12-14=as!}}
_ = cell as SomeCell

_ = plainObj as CopyableNSObject // expected-error {{'NSObject' is not convertible to 'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol'); did you mean to use 'as!' to force downcast?}} {{16-18=as!}}
_ = plainObj as CopyableNSObject // expected-error {{'NSObject' is not convertible to 'CopyableNSObject' (aka 'NSCopying'); did you mean to use 'as!' to force downcast?}} {{16-18=as!}}
_ = plainCell as CopyableSomeCell // FIXME: This is not really typesafe.
}

Expand Down
17 changes: 17 additions & 0 deletions test/Compatibility/nsobjectprotocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %target-typecheck-verify-swift -swift-version 3
// RUN: %target-typecheck-verify-swift -swift-version 4
// REQUIRES: objc_interop

import ObjectiveC
import Foundation

class X: NSObjectProtocol { } // expected-warning{{'NSObjectProtocol' is deprecated: all classes implicitly conform to the 'NSObject' protocol}}
// expected-note@-1{{use 'AnyObject' instead}}
// expected-warning@-2{{conformance of class 'X' to 'AnyObject' is redundant}}

protocol P: NSObjectProtocol { } // expected-warning{{'NSObjectProtocol' is deprecated: all classes implicitly conform to the 'NSObject' protocol}}
// expected-note@-1{{use 'AnyObject' instead}}

func composition(_: NSCoding & NSObjectProtocol) { } // expected-warning{{'NSObjectProtocol' is deprecated: all classes implicitly conform to the 'NSObject' protocol}}
// expected-note@-1{{use 'AnyObject' instead}}

3 changes: 2 additions & 1 deletion test/IDE/dump_swift_lookup_tables_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
// CHECK-NOT: lookup table
// CHECK: NSObject:
// CHECK-NEXT: TU: NSObject
// CHECK-NEXT: NSObjectProtocol:
// CHECK: __NSObjectProtocol:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the extra "Protocol" coming from? Does that kick in before the SwiftPrivate part?

// CHECK-NEXT: TU: NSObject
// CHECK: responds:
// CHECK-NEXT: -[NSObject respondsToSelector:]


// CHECK-LABEL: <<Bridging header lookup table>>
// CHECK-NEXT: Base name -> entry mappings:
// CHECK-NEXT: CCItem:
Expand Down
2 changes: 1 addition & 1 deletion test/IDE/importProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// REQUIRES: objc_interop

// CHECK: protocol ImportedProtocolBase : NSObjectProtocol {
// CHECK: protocol ImportedProtocolBase {
// CHECK: }
// CHECK: typealias ImportedProtocolBase_t = ImportedProtocolBase
// CHECK: protocol IPSub : ImportedProtocolBase {
Expand Down
4 changes: 2 additions & 2 deletions test/IDE/print_clang_ObjectiveC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
// NEGATIVE-WITHOUT-FORWARD-DECLS-NOT: var description
// NEGATIVE-NOT: NSCoder

// CHECK-LABEL: protocol NSObjectProtocol {
// CHECK-LABEL: protocol __NSObjectProtocol {
// CHECK-DAG: var superclass: AnyClass? { get }
// CHECK-DAG: func zone() -> NSZone
// CHECK-WITH-FORWARD-DECLS-DAG: var description: String { get }
// CHECK: {{^[}]$}}

// CHECK-LABEL: class NSObject : NSObjectProtocol {
// CHECK-LABEL: class NSObject {
// CHECK-DAG: func copy() -> Any
// CHECK-DAG: class func hash() -> Int
// CHECK-WITH-FORWARD-DECLS-DAG: class func description() -> String
Expand Down
2 changes: 1 addition & 1 deletion test/IDE/print_omit_needless_words.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
// CHECK-FOUNDATION: func doSomething(with: NSCopying)

// Note: NSObject<Proto> treated as "Proto".
// CHECK-FOUNDATION: func doSomethingElse(with: NSCopying & NSObjectProtocol)
// CHECK-FOUNDATION: func doSomethingElse(with: NSCopying)

// Note: Function type -> "Function".
// CHECK-FOUNDATION: func sort(_: @escaping @convention(c) (Any, Any) -> Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,7 @@ extension NSObject : Equatable, Hashable {
public func == (lhs: NSObject, rhs: NSObject) -> Bool {
return lhs.isEqual(rhs)
}

@available(*, deprecated, renamed: "AnyObject",
message: "all classes implicitly conform to the 'NSObject' protocol")
public typealias NSObjectProtocol = AnyObject
4 changes: 4 additions & 0 deletions test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,7 @@ extension NSObject : Equatable, Hashable {
public func == (lhs: NSObject, rhs: NSObject) -> Bool {
return lhs.isEqual(rhs)
}

@available(*, deprecated, renamed: "AnyObject",
message: "all classes implicitly conform to the 'NSObject' protocol")
public typealias NSObjectProtocol = AnyObject
10 changes: 4 additions & 6 deletions test/PrintAsObjC/classes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,13 @@ class ClassWithCustomName2 {}
class ClassWithCustomNameSub : ClassWithCustomName {}


// CHECK-LABEL: @interface ClassWithNSObjectProtocol <NSObject>
// CHECK-LABEL: @interface ClassWithNSObjectProtocol
// CHECK-NEXT: @property (nonatomic, readonly, copy) NSString * _Nonnull description;
// CHECK-NEXT: - (BOOL)conformsToProtocol:(Protocol * _Nonnull)_ SWIFT_WARN_UNUSED_RESULT;
// CHECK-NEXT: - (BOOL)isKindOfClass:(Class _Nonnull)aClass SWIFT_WARN_UNUSED_RESULT;
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
// CHECK-NEXT: @end
@objc class ClassWithNSObjectProtocol : NSObjectProtocol {
@objc class ClassWithNSObjectProtocol {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer testing the same thing; please have it conform to something else. (Preferably something where the Objective-C name doesn't match the Swift name.)

@objc var description: String { return "me" }
@objc(conformsToProtocol:)
func conforms(to _: Protocol) -> Bool { return false }
Expand Down Expand Up @@ -355,9 +355,9 @@ typealias AliasForNSRect = NSRect
// NEGATIVE-NOT: @interface NSObject
class MyObject : NSObject {}

// CHECK-LABEL: @protocol MyProtocol <NSObject>
// CHECK-LABEL: @protocol MyProtocol
// CHECK-NEXT: @end
@objc protocol MyProtocol : NSObjectProtocol {}
@objc protocol MyProtocol {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here, since we're testing printing.


// CHECK-LABEL: @protocol MyProtocolMetaOnly;
// CHECK-LABEL: @interface MyProtocolMetaCheck
Expand Down Expand Up @@ -521,7 +521,6 @@ public class NonObjCClass { }
// CHECK-NEXT: @property (nonatomic, copy) IBOutletCollection(Properties) NSArray<Properties *> * _Null_unspecified outletCollection;
// CHECK-NEXT: @property (nonatomic, copy) IBOutletCollection(CustomName) NSArray<CustomName *> * _Nullable outletCollectionOptional;
// CHECK-NEXT: @property (nonatomic, copy) IBOutletCollection(id) NSArray * _Nullable outletCollectionAnyObject;
// CHECK-NEXT: @property (nonatomic, copy) IBOutletCollection(id) NSArray<id <NSObject>> * _Nullable outletCollectionProto;
// CHECK-NEXT: SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly) NSInteger staticInt;)
// CHECK-NEXT: + (NSInteger)staticInt SWIFT_WARN_UNUSED_RESULT;
// CHECK-NEXT: SWIFT_CLASS_PROPERTY(@property (nonatomic, class, copy) NSString * _Nonnull staticString;)
Expand Down Expand Up @@ -614,7 +613,6 @@ public class NonObjCClass { }
@IBOutlet var outletCollection: [Properties]!
@IBOutlet var outletCollectionOptional: [ClassWithCustomName]? = []
@IBOutlet var outletCollectionAnyObject: [AnyObject]?
@IBOutlet var outletCollectionProto: [NSObjectProtocol]?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here.


@objc static let staticInt = 2
@objc static var staticString = "Hello"
Expand Down
4 changes: 2 additions & 2 deletions test/stdlib/RuntimeObjC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -695,8 +695,8 @@ Reflection.test("MetatypeMirror") {
dump(compositionConcreteMetatype, to: &output)
expectEqual(expectedComposition, output)

let objcDefinedProtoType = NSObjectProtocol.self
expectEqual(String(describing: objcDefinedProtoType), "NSObject")
let objcDefinedProtoType = NSCoding.self
expectEqual(String(describing: objcDefinedProtoType), "NSCoding")
}
}

Expand Down