blob: ae369ca717b2930d03efd47d53da27ea7950bad7 [file] [log] [blame]
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -Xfrontend -disable-objc-attr-requires-foundation-module -o %t/main
// RUN: %target-codesign %t/main
// RUN: %target-run %t/main | %FileCheck %s
// RUN: sed -e 's/required//g' < %s > %t/without_required.swift
// RUN: %target-build-swift %t/without_required.swift -Xfrontend -disable-objc-attr-requires-foundation-module -o %t/without_required
// RUN: %target-codesign %t/without_required
// RUN: %target-run %t/without_required | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: objc_interop
import Darwin
class Base {
init(swift: ()) {
print("\(#function) \(type(of: self))")
}
@objc(initAsObjC) required init(objc: ()) {
print("\(#function) \(type(of: self))")
}
convenience init(swiftToSwift: ()) {
print("\(#function) \(type(of: self))")
self.init(swift: ())
}
@objc convenience required init(objcToSwift: ()) {
print("\(#function) \(type(of: self))")
self.init(swift: ())
}
convenience init(swiftToObjC: ()) {
print("\(#function) \(type(of: self))")
self.init(objc: ())
}
@objc convenience required init(objcToObjC: ()) {
print("\(#function) \(type(of: self))")
self.init(objc: ())
}
convenience init(swiftToSwiftConvenience: ()) {
print("\(#function) \(type(of: self))")
self.init(swiftToSwift: ())
}
@objc convenience required init(objcToSwiftConvenience: ()) {
print("\(#function) \(type(of: self))")
self.init(swiftToSwift: ())
}
convenience init(swiftToObjCConvenience: ()) {
print("\(#function) \(type(of: self))")
self.init(objcToObjC: ())
}
@objc convenience required init(objcToObjCConvenience: ()) {
print("\(#function) \(type(of: self))")
self.init(objcToObjC: ())
}
}
class Sub: Base {}
@objc protocol ForceObjCDispatch {
@objc(initAsObjC) init(objc: ())
init(objcToSwift: ())
init(objcToObjC: ())
init(objcToSwiftConvenience: ())
init(objcToObjCConvenience: ())
}
// Replace swift_allocObject so that we can keep track of what gets allocated.
var baseCounter = 0
var subCounter = 0
typealias AllocObjectType =
@convention(c) (UnsafeRawPointer, Int, Int) -> UnsafeMutableRawPointer
let allocObjectImpl =
dlsym(UnsafeMutableRawPointer(bitPattern: -1), "_swift_allocObject")
.assumingMemoryBound(to: AllocObjectType.self)
/// Like `ObjectIdentifier.init(Any.Type)`, but with a pointer as the
/// destination type.
func asUnsafeRawPointer(_ someClass: AnyObject.Type) -> UnsafeRawPointer {
let opaque = Unmanaged.passUnretained(someClass as AnyObject).toOpaque()
return UnsafeRawPointer(opaque)
}
let originalAllocObject = allocObjectImpl.pointee
allocObjectImpl.pointee = {
switch $0 {
case asUnsafeRawPointer(Base.self):
baseCounter += 1
case asUnsafeRawPointer(Sub.self):
subCounter += 1
default:
break
}
return originalAllocObject($0, $1, $2)
}
/// Checks that `op` performs `base` allocations of Base and `sub` allocations
/// of Sub.
@inline(never)
func check(base: Int = 0, sub: Int = 0,
file: StaticString = #file, line: UInt = #line,
op: () -> AnyObject) {
baseCounter = 0
subCounter = 0
_ = op()
precondition(baseCounter == base,
"expected \(base) Base instances, got \(baseCounter)",
file: file, line: line)
precondition(subCounter == sub,
"expected \(sub) Sub instances, got \(subCounter)",
file: file, line: line)
}
// CHECK: START
print("START")
// Check that this whole setup works.
// CHECK-NEXT: init(swift:) Base
check(base: 1) { Base(swift: ()) }
// CHECK-NEXT: init(swift:) Sub
check(sub: 1) { Sub(swift: ()) }
// CHECK-NEXT: init(objc:) Base
check(base: 1) { Base(objc: ()) }
// CHECK-NEXT: init(objc:) Sub
check(sub: 1) { Sub(objc: ()) }
// CHECK-NEXT: init(swiftToSwift:) Sub
// CHECK-NEXT: init(swift:) Sub
check(sub: 1) { Sub(swiftToSwift: ()) }
// CHECK-NEXT: init(objcToSwift:) Sub
// CHECK-NEXT: init(swift:) Sub
check(sub: 2) { Sub(objcToSwift: ()) }
// CHECK-NEXT: init(swiftToObjC:) Sub
// CHECK-NEXT: init(objc:) Sub
check(sub: 1) { Sub(swiftToObjC: ()) }
// CHECK-NEXT: init(objcToObjC:) Sub
// CHECK-NEXT: init(objc:) Sub
check(sub: 1) { Sub(objcToObjC: ()) }
// CHECK-NEXT: init(swiftToSwiftConvenience:) Sub
// CHECK-NEXT: init(swiftToSwift:) Sub
// CHECK-NEXT: init(swift:) Sub
check(sub: 1) { Sub(swiftToSwiftConvenience: ()) }
// CHECK-NEXT: init(objcToSwiftConvenience:) Sub
// CHECK-NEXT: init(swiftToSwift:) Sub
// CHECK-NEXT: init(swift:) Sub
check(sub: 2) { Sub(objcToSwiftConvenience: ()) }
// CHECK-NEXT: init(swiftToObjCConvenience:) Sub
// CHECK-NEXT: init(objcToObjC:) Sub
// CHECK-NEXT: init(objc:) Sub
check(sub: 1) { Sub(swiftToObjCConvenience: ()) }
// CHECK-NEXT: init(objcToObjCConvenience:) Sub
// CHECK-NEXT: init(objcToObjC:) Sub
// CHECK-NEXT: init(objc:) Sub
check(sub: 1) { Sub(objcToObjCConvenience: ()) }
// Force ObjC dispatch without conforming Sub or Base to the protocol,
// because it's possible that `required` perturbs things and we want to test
// both ways.
let SubAsObjC = unsafeBitCast(Sub.self as AnyObject,
to: ForceObjCDispatch.Type.self)
// CHECK-NEXT: init(objc:) Sub
check(sub: 1) { SubAsObjC.init(objc: ()) }
// CHECK-NEXT: init(objcToSwift:) Sub
// CHECK-NEXT: init(swift:) Sub
check(sub: 2) { SubAsObjC.init(objcToSwift: ()) }
// CHECK-NEXT: init(objcToObjC:) Sub
// CHECK-NEXT: init(objc:) Sub
check(sub: 1) { SubAsObjC.init(objcToObjC: ()) }
// CHECK-NEXT: init(objcToSwiftConvenience:) Sub
// CHECK-NEXT: init(swiftToSwift:) Sub
// CHECK-NEXT: init(swift:) Sub
check(sub: 2) { SubAsObjC.init(objcToSwiftConvenience: ()) }
// CHECK-NEXT: init(objcToObjCConvenience:) Sub
// CHECK-NEXT: init(objcToObjC:) Sub
// CHECK-NEXT: init(objc:) Sub
check(sub: 1) { SubAsObjC.init(objcToObjCConvenience: ()) }
// CHECK-NEXT: END
print("END")