//===--- subclass_existentials.swift --------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out // REQUIRES: executable_test import StdlibUnittest protocol P { init(protocolInit: ()) func protocolMethodReturnsSelf() -> Self static func staticProtocolMethodReturnsSelf() -> Self var protocolProperty: Int { get set } static var staticProtocolProperty: Int { get set } subscript(protocolKey protocolKey: U) -> Int { get set } } protocol R {} extension R { func extensionMethod() -> Self { return self } } var globalVar = 8 class Base : R { var x: T var y: T var token = LifetimeTracked(0) var dict: [AnyHashable : T] = [:] required init(x: T, y: T) { self.x = x self.y = y } func classMethodReturnsSelf() -> Self { return self } func finalClassMethod() -> (T, T) { return (x, y) } func nonFinalClassMethod() -> (T, T) { return (x, y) } var propertyToOverride: Int = -8 class var classPropertyToOverride: Int { get { return globalVar } set { globalVar = newValue } } subscript(key: U) -> T { get { return dict[key]! } set { self.dict[key] = newValue } } } class Derived : Base, P { required init(x: Int, y: Int) { super.init(x: x, y: y) } override func nonFinalClassMethod() -> (Int, Int) { return (y, x) } override var propertyToOverride: Int { get { return super.propertyToOverride * 2 } set { super.propertyToOverride = newValue / 2 } } class override var classPropertyToOverride: Int { get { return super.classPropertyToOverride * 2 } set { super.classPropertyToOverride = newValue / 2 } } convenience required init(protocolInit: ()) { self.init(x: 10, y: 20) } func protocolMethodReturnsSelf() -> Self { return self } static func staticProtocolMethodReturnsSelf() -> Self { return self.init(x: -1000, y: -2000) } var protocolProperty: Int = 100 static var staticProtocolProperty: Int = 2000 subscript(protocolKey protocolKey: U) -> Int { get { return dict[protocolKey]! } set { self.dict[protocolKey] = newValue } } } protocol Q : class {} var SubclassExistentialsTestSuite = TestSuite("SubclassExistentials") SubclassExistentialsTestSuite.test("Metadata instantiation") { expectTrue((Base & Base).self == Base.self) expectTrue((Base & Any).self == Base.self) expectTrue((Base & Q).self == (Q & Base).self) expectTrue((Base & P & Q).self == (P & Base & Q).self) expectTrue((P & Q & Base).self == (Q & Base & P).self) expectTrue((P & Q).self == (P & Q & AnyObject).self) expectTrue((P & Q).self == (Q & P & AnyObject).self) expectTrue((Base & Q).self == (Q & Base & AnyObject).self) expectFalse((R & AnyObject).self == R.self) } SubclassExistentialsTestSuite.test("Metadata to string") { expectEqual("Base & P", String(describing: (Base & P).self)) expectEqual("Base & P & Q", String(describing: (Base & P & Q).self)) } SubclassExistentialsTestSuite.test("Call instance methods") { do { let value: Base & P = Derived(x: 123, y: 321) // Basic method calls expectTrue(value === value.classMethodReturnsSelf()) expectTrue((123, 321) == value.finalClassMethod()) expectTrue((321, 123) == value.nonFinalClassMethod()) expectTrue(value === value.extensionMethod()) // Partial application do { let fn = value.classMethodReturnsSelf expectTrue(value === fn()) } do { let fn = value.finalClassMethod expectTrue((123, 321) == fn()) } do { let fn = value.nonFinalClassMethod expectTrue((321, 123) == fn()) } } expectEqual(0, LifetimeTracked.instances) } SubclassExistentialsTestSuite.test("Access instance properties") { do { let value: Base & P = Derived(x: 123, y: 321) expectEqual(-16, value.propertyToOverride) value.propertyToOverride += 4 expectEqual(-12, value.propertyToOverride) value.propertyToOverride += 1 expectEqual(-10, value.propertyToOverride) } expectEqual(0, LifetimeTracked.instances) } SubclassExistentialsTestSuite.test("Access subscript") { do { let value: Base & P = Derived(x: 123, y: 321) value[1] = 2 value["hi"] = 20 expectEqual(2, value[1]) expectEqual(20, value["hi"]) value[1] += 1 value["hi"] += 1 expectEqual(3, value[1]) expectEqual(21, value["hi"]) } expectEqual(0, LifetimeTracked.instances) } SubclassExistentialsTestSuite.test("Call static methods") { do { let value: Base & P = Derived(x: 123, y: 321) let metatype: (Base & P).Type = type(of: value) let newValue = metatype.init(x: 256, y: 512) expectTrue(newValue === newValue.classMethodReturnsSelf()) expectTrue((256, 512) == newValue.finalClassMethod()) expectTrue((512, 256) == newValue.nonFinalClassMethod()) do { let fn = metatype.init(x:y:) let newValue = fn(1, 2) expectTrue((1, 2) == newValue.finalClassMethod()) } } expectEqual(0, LifetimeTracked.instances) } SubclassExistentialsTestSuite.test("Access static properties") { do { let value: Base & P = Derived(x: 123, y: 321) expectEqual(16, type(of: value).classPropertyToOverride) type(of: value).classPropertyToOverride += 4 expectEqual(20, type(of: value).classPropertyToOverride) type(of: value).classPropertyToOverride += 1 expectEqual(20, type(of: value).classPropertyToOverride) } expectEqual(0, LifetimeTracked.instances) } SubclassExistentialsTestSuite.test("Call protocol instance methods") { do { let value: Base & P = Derived(x: 123, y: 321) // Basic method calls expectTrue(value === value.protocolMethodReturnsSelf()) // Partial application do { let fn = value.protocolMethodReturnsSelf expectTrue(value === fn()) } } expectEqual(0, LifetimeTracked.instances) } SubclassExistentialsTestSuite.test("Access protocol instance properties") { do { var value: Base & P = Derived(x: 123, y: 321) expectEqual(100, value.protocolProperty) value.protocolProperty += 1 expectEqual(101, value.protocolProperty) } expectEqual(0, LifetimeTracked.instances) } SubclassExistentialsTestSuite.test("Access protocol subscript") { do { var value: Base & P = Derived(x: 123, y: 321) value[protocolKey: 1] = 2 value[protocolKey: "hi"] = 20 expectEqual(2, value[protocolKey: 1]) expectEqual(20, value[protocolKey: "hi"]) value[protocolKey: 1] += 1 value[protocolKey: "hi"] += 1 expectEqual(3, value[protocolKey: 1]) expectEqual(21, value[protocolKey: "hi"]) } expectEqual(0, LifetimeTracked.instances) } // Note: in optimized builds, these tests will get optimized down and // exercise a totally different code path. That's fine. SubclassExistentialsTestSuite.test("Scalar downcast to subclass existential") { do { let baseInt: Base = Derived(x: 123, y: 321) let derived = baseInt as? (Base & P) expectEqual(123, derived!.x) expectEqual(321, derived!.y) } do { let p: P = Derived(x: 123, y: 321) let result = p as? (Base & P) expectEqual(123, result!.x) expectEqual(321, result!.y) } do { let r: R = Derived(x: 123, y: 321) let result = r as? (Base & P) expectEqual(123, result!.x) expectEqual(321, result!.y) } do { let baseInt: Base = Derived(x: 123, y: 321) let result = baseInt as? (Base & P) expectEqual(123, result!.x) expectEqual(321, result!.y) } expectEqual(0, LifetimeTracked.instances) } func cast(_ t: T, to: U.Type) -> U? { return t as? U } // Note: in optimized builds, these tests will get optimized down and // exercise a totally different code path. That's fine. SubclassExistentialsTestSuite.test("Dynamic downcast to subclass existential") { do { let baseInt: Base = Derived(x: 123, y: 321) let derived = cast(baseInt, to: (Base & P).self) expectEqual(123, derived!.x) expectEqual(321, derived!.y) } do { let p: P = Derived(x: 123, y: 321) let result = cast(p, to: (Base & P).self) expectEqual(123, result!.x) expectEqual(321, result!.y) } do { let r: R = Derived(x: 123, y: 321) let result = cast(r, to: (Base & P).self) expectEqual(123, result!.x) expectEqual(321, result!.y) } do { let baseInt: Base = Derived(x: 123, y: 321) let result = cast(baseInt, to: (Base & P).self) expectEqual(123, result!.x) expectEqual(321, result!.y) } expectEqual(0, LifetimeTracked.instances) } class ConformsToP : P { var token = LifetimeTracked(0) required init(protocolInit: ()) {} func protocolMethodReturnsSelf() -> Self { return self } static func staticProtocolMethodReturnsSelf() -> Self { return self.init(protocolInit: ()) } var protocolProperty: Int = 100 static var staticProtocolProperty: Int = 2000 subscript(protocolKey protocolKey: U) -> Int { get { return 0 } set {} } } SubclassExistentialsTestSuite.test("Failing scalar downcast to subclass existential") { do { let baseInt: Base = Base(x: 123, y: 321) expectNil(baseInt as? (Base & P)) expectFalse(baseInt is (Base & P)) } do { let r: R = Base(x: 123, y: 321) expectNil(r as? (Base & P)) expectFalse(r is (Base & P)) } do { let conformsToP = ConformsToP(protocolInit: ()) expectNil(conformsToP as? (Base & P)) expectFalse(conformsToP is (Base & P)) } } SubclassExistentialsTestSuite.test("Failing dynamic downcast to subclass existential") { do { let baseInt: Base = Base(x: 123, y: 321) expectNil(cast(baseInt, to: (Base & P).self)) } do { let r: R = Base(x: 123, y: 321) expectNil(cast(r, to: (Base & P).self)) } do { let conformsToP = ConformsToP(protocolInit: ()) expectNil(cast(conformsToP, to: (Base & P).self)) } } // This test triggers https://github.com/apple/swift/issues/43427 // (rdar://problem/25318716) on macOS 10.9 and iOS 7. Disable it for now when // testing on those versions. if #available(macOS 10.10, iOS 8, *) { runAllTests() } else { runNoTests() }