| // RUN: %empty-directory(%t) |
| // RUN: %gyb %s -o %t/ArrayTraps.swift |
| // RUN: %line-directive %t/ArrayTraps.swift -- %target-build-swift %t/ArrayTraps.swift -o %t/a.out_Debug -Onone |
| // RUN: %line-directive %t/ArrayTraps.swift -- %target-build-swift %t/ArrayTraps.swift -o %t/a.out_Release -O |
| // |
| // RUN: %target-codesign %t/a.out_Debug |
| // RUN: %target-codesign %t/a.out_Release |
| // RUN: %line-directive %t/ArrayTraps.swift -- %target-run %t/a.out_Debug |
| // RUN: %line-directive %t/ArrayTraps.swift -- %target-run %t/a.out_Release |
| // REQUIRES: executable_test |
| // REQUIRES: objc_interop |
| |
| import StdlibUnittest |
| import Foundation |
| |
| let testSuiteSuffix = _isDebugAssertConfiguration() ? "_debug" : "_release" |
| |
| var ArrayTraps = TestSuite("ArrayTraps" + testSuiteSuffix) |
| |
| class Base { } |
| class Derived : Base { } |
| class Derived2 : Derived { } |
| |
| ArrayTraps.test("downcast1") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .code { |
| let ba: [Base] = [ Derived(), Base() ] |
| let da = ba as! [Derived] |
| let d0 = da[0] |
| expectCrashLater() |
| _ = da[1] |
| } |
| |
| ArrayTraps.test("downcast2") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .code { |
| let a: [AnyObject] = ["String" as NSString, 1 as NSNumber] |
| let sa = a as! [NSString] |
| let s0 = sa[0] |
| expectCrashLater() |
| _ = sa[1] |
| } |
| |
| ArrayTraps.test("downcast3") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .code { |
| let ba: [Base] = [ Derived2(), Derived(), Base() ] |
| let d2a = ba as! [Derived2] |
| let d2a0 = d2a[0] |
| let d1a = d2a as [Derived] |
| let d1a0 = d1a[0] |
| let d1a1 = d1a[1] |
| expectCrashLater() |
| _ = d1a[2] |
| } |
| |
| @objc protocol ObjCProto { } |
| class ObjCBase : NSObject, ObjCProto { } |
| class ObjCDerived : ObjCBase { } |
| |
| ArrayTraps.test("downcast4") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .code { |
| let ba: [ObjCProto] = [ ObjCDerived(), ObjCBase() ] |
| let da = ba as! [ObjCDerived] |
| let d0 = da[0] |
| expectCrashLater() |
| _ = da[1] |
| } |
| |
| ArrayTraps.test("bounds_with_downcast") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| "Fatal error: Index out of range" : "") |
| .code { |
| let ba: [Base] = [ Derived(), Base() ] |
| let da = ba as! [Derived] |
| expectCrashLater() |
| let x = da[2] |
| } |
| |
| var ArraySemanticOptzns = TestSuite("ArraySemanticOptzns" + testSuiteSuffix) |
| |
| class BaseClass { |
| } |
| |
| class ElementClass : BaseClass { |
| var val: String |
| init(_ x: String) { |
| val = x |
| } |
| } |
| |
| class ViolateInoutSafetySwitchToObjcBuffer { |
| final var anArray: [ElementClass] = [] |
| |
| let nsArray = NSArray( |
| objects: ElementClass("a"), ElementClass("b"), ElementClass("c")) |
| |
| @inline(never) |
| func accessArrayViaInoutViolation() { |
| anArray = nsArray as! [ElementClass] |
| } |
| |
| @inline(never) |
| func runLoop(_ A: inout [ElementClass]) { |
| // Simulate what happens if we hoist array properties out of a loop and the |
| // loop calls a function that violates inout safety and overrides the array. |
| let isNativeTypeChecked = A._hoistableIsNativeTypeChecked() |
| for i in 0..<A.count { |
| let t = A._checkSubscript( |
| i, wasNativeTypeChecked: isNativeTypeChecked) |
| _ = A._getElement( |
| i, wasNativeTypeChecked: isNativeTypeChecked, matchingSubscriptCheck: t) |
| accessArrayViaInoutViolation() |
| } |
| } |
| |
| @inline(never) |
| func inoutViolation() { |
| anArray = [ ElementClass("1"), ElementClass("2"), ElementClass("3") ] |
| runLoop(&anArray) |
| } |
| } |
| |
| ArraySemanticOptzns.test("inout_rule_violated_isNativeBuffer") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| "Fatal error: inout rules were violated: the array was overwritten" : "") |
| .code { |
| let v = ViolateInoutSafetySwitchToObjcBuffer() |
| expectCrashLater() |
| v.inoutViolation() |
| } |
| |
| class ViolateInoutSafetyNeedElementTypeCheck { |
| final var anArray : [ElementClass] = [] |
| |
| @inline(never) |
| func accessArrayViaInoutViolation() { |
| // Overwrite the array with one that needs an element type check. |
| let ba: [BaseClass] = [ BaseClass(), BaseClass() ] |
| anArray = ba as! [ElementClass] |
| } |
| |
| @inline(never) |
| func runLoop(_ A: inout [ElementClass]) { |
| // Simulate what happens if we hoist array properties out of a loop and the |
| // loop calls a function that violates inout safety and overrides the array. |
| let isNativeTypeChecked = A._hoistableIsNativeTypeChecked() |
| for i in 0..<A.count { |
| let t = A._checkSubscript( |
| i, wasNativeTypeChecked: isNativeTypeChecked) |
| _ = A._getElement( |
| i, wasNativeTypeChecked: isNativeTypeChecked, matchingSubscriptCheck: t) |
| accessArrayViaInoutViolation() |
| } |
| } |
| |
| @inline(never) |
| func inoutViolation() { |
| anArray = [ ElementClass("1"), ElementClass("2"), ElementClass("3")] |
| runLoop(&anArray) |
| } |
| } |
| |
| ArraySemanticOptzns.test("inout_rule_violated_needsElementTypeCheck") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| "Fatal error: inout rules were violated: the array was overwritten" : "") |
| .code { |
| let v = ViolateInoutSafetyNeedElementTypeCheck() |
| expectCrashLater() |
| v.inoutViolation() |
| } |
| |
| runAllTests() |