// 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 import StdlibUnittest let testSuiteSuffix = _isDebugAssertConfiguration() ? "_debug" : "_release" %{ # We test for bounds-checking traps for both reading and writing # both single elements and slices of all three different array # types. array_types = ('Array', 'ContiguousArray', 'ArraySlice') def bounds_trap(index, expr_to_write): global io return 'expectCrashLater()\n' + ( 'let x = a[%s]' % index if io == 'read' else 'a[%s] = %s' % (index, expr_to_write)) }% % for ArrayTy in array_types: var ${ArrayTy}Traps = TestSuite("${ArrayTy}Traps" + testSuiteSuffix) var ${ArrayTy}LowerBoundOutOfRangeErrorMsg = ( % if ArrayTy in ['Array', 'ContiguousArray']: "Negative ${ArrayTy} index is out of range" % else: "${ArrayTy} index is out of range (before startIndex)" % end ) var ${ArrayTy}BoundsErrorMsg = ( % if ArrayTy in ['Array', 'ContiguousArray']: "Index out of range" % else: "Index out of bounds" % end ) % for io in ['read', 'write']: ${ArrayTy}Traps.test("bounds1/${io}") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? ${ArrayTy}BoundsErrorMsg : "") .code { var a: ${ArrayTy} = [] ${bounds_trap(index='0', expr_to_write='1')} } ${ArrayTy}Traps.test("bounds2/${io}") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? ${ArrayTy}BoundsErrorMsg : "") .code { var a: ${ArrayTy} = [] ${bounds_trap(index='100', expr_to_write='1')} } ${ArrayTy}Traps.test("bounds3/${io}") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? ${ArrayTy}BoundsErrorMsg : "") .code { var a: ${ArrayTy} = [ 10, 20, 30 ] ${bounds_trap(index='3', expr_to_write='1')} } ${ArrayTy}Traps.test("sliceBounds0/${io}") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? ${ArrayTy}LowerBoundOutOfRangeErrorMsg : "") .code { var a: ${ArrayTy} = [] ${bounds_trap(index='-1..<1', expr_to_write='ArraySlice()')} } ${ArrayTy}Traps.test("sliceBounds1/${io}") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? ${ArrayTy}LowerBoundOutOfRangeErrorMsg : "") .code { var a: ${ArrayTy} = [ 1 ] ${bounds_trap(index='-1..<1', expr_to_write='ArraySlice()')} } ${ArrayTy}Traps.test("sliceBounds2/${io}") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? "${ArrayTy} index is out of range" : "") .code { var a: ${ArrayTy} = [ 1 ] ${bounds_trap(index='0..<2', expr_to_write='ArraySlice()')} } ${ArrayTy}Traps.test("sliceBounds3/${io}") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? "${ArrayTy} index is out of range" : "") .code { var a: ${ArrayTy} = [ 1 ] ${bounds_trap(index='1..<2', expr_to_write='ArraySlice()')} } % end ${ArrayTy}Traps.test("PopFromEmpty") .crashOutputMatches(_isDebugAssertConfiguration() ? "Can't remove last element from an empty collection" : "") .code { var a: ${ArrayTy} = [] expectCrashLater() a.removeLast() } % for index in -1, 2: ${ArrayTy}Traps.test("${'insert(_:at:)/%s' % index}") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? % if index < 0: ${ArrayTy}LowerBoundOutOfRangeErrorMsg % else: "${ArrayTy} index is out of range" %end : "") .code { var a: ${ArrayTy} = [42] expectCrashLater() a.insert(3, at: ${index}) } % end % for index in -1, 1, 2: ${ArrayTy}Traps.test("${'remove(at:)/%s' % index}") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? ${ArrayTy}BoundsErrorMsg : "") .code { var a: ${ArrayTy} = [42] expectCrashLater() a.remove(at: ${index}) } % end % end ArrayTraps.test("unsafeLength") .skip(.custom( { !_isDebugAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -O or -Ounchecked")) .crashOutputMatches(_isDebugAssertConfiguration() ? "UnsafeBufferPointer with negative count" : "") .code { var a = [ 42, 77, 88 ] expectEqual(42, a.withUnsafeBufferPointer({ $0[0] })) expectCrashLater() _ = a.withUnsafeBufferPointer { UnsafeBufferPointer(start: $0.baseAddress, count: -1) } } runAllTests()