From 04c7d946b250f1dada435870d3d7c8327a703bb0 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Fri, 28 Oct 2016 14:31:32 -0700 Subject: [PATCH 001/153] Migrate _copyContents to be called from UnsafeMutableBufferPointer with checks for overrun. --- .../CheckSequenceInstance.swift.gyb | 18 ++-- .../LoggingWrappers.swift.gyb | 8 +- stdlib/public/SDK/Foundation/Data.swift | 4 +- stdlib/public/core/Arrays.swift.gyb | 85 ++++--------------- .../public/core/ContiguousArrayBuffer.swift | 12 ++- .../core/ExistentialCollection.swift.gyb | 22 +++-- stdlib/public/core/LazyCollection.swift.gyb | 11 +-- stdlib/public/core/Sequence.swift | 28 +++--- stdlib/public/core/SequenceWrapper.swift | 6 +- .../public/core/UnsafeBufferPointer.swift.gyb | 18 ++++ stdlib/public/core/UnsafePointer.swift.gyb | 8 +- .../source-stability.swift.expected | 3 + test/stdlib/UnsafeRawPointer.swift | 3 +- .../stdlib/ExistentialCollection.swift.gyb | 2 +- validation-test/stdlib/Lazy.swift.gyb | 20 ++--- 15 files changed, 121 insertions(+), 127 deletions(-) diff --git a/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb b/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb index f97fe0366adc4..f5e4b7c7c04a5 100644 --- a/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb +++ b/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb @@ -86,16 +86,14 @@ public func checkSequence< _ = sequence._preprocessingPass { () -> Void in var count = 0 for _ in sequence { count += 1 } - let buf = UnsafeMutablePointer.allocate(capacity: count) - let end = sequence._copyContents(initializing: buf) - expectTrue(end == buf + count, "_copyContents returned the wrong value") - var j = expected.startIndex - for i in 0..<(end - buf) { - expectTrue(sameValue(expected[j], buf[i])) - j = expected.index(after: j) - } - buf.deinitialize(count: end - buf) - buf.deallocate(capacity: count) + let ptr = UnsafeMutablePointer.allocate(capacity: count) + let buf = UnsafeMutableBufferPointer(start: ptr, count: count) + var remainders = sequence._copyContents(initializing: buf) + expectTrue(remainders == nil || remainders!.next() == nil, + "_copyContents returned unwritten elements") + expectEqualSequence(expected, buf, ${trace}, sameValue: sameValue) + ptr.deinitialize(count: count) + ptr.deallocate(capacity: count) } // Test `_copyToContiguousArray()` if we can do so diff --git a/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb b/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb index 9363c17c5542e..25507e8c791e1 100644 --- a/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb +++ b/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb @@ -215,7 +215,7 @@ public struct ${Self}< public typealias Iterator = LoggingIterator - public func makeIterator() -> LoggingIterator { + public func makeIterator() -> Iterator { Log.makeIterator[selfType] += 1 return LoggingIterator(wrapping: base.makeIterator()) } @@ -321,10 +321,10 @@ public struct ${Self}< /// Copy a Sequence into an array. public func _copyContents( - initializing ptr: UnsafeMutablePointer - ) -> UnsafeMutablePointer { + initializing buf: UnsafeMutableBufferPointer + ) -> Iterator? { Log._copyContents[selfType] += 1 - return base._copyContents(initializing: ptr) + return base._copyContents(initializing: buf).map(Iterator.init) } % if Kind in ['Collection', 'MutableCollection', 'RangeReplaceableCollection']: diff --git a/stdlib/public/SDK/Foundation/Data.swift b/stdlib/public/SDK/Foundation/Data.swift index 8a71370ff2609..10a8da32afcd7 100644 --- a/stdlib/public/SDK/Foundation/Data.swift +++ b/stdlib/public/SDK/Foundation/Data.swift @@ -551,7 +551,9 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl } if replacementCount != 0 { - newElements._copyContents(initializing: bytes + start) + let buf = UnsafeMutableBufferPointer(start: bytes + start, count: numericCast(newElements.count)) + // FIXME: is this guaranteed to return no residual elements? + let _ = newElements._copyContents(initializing: buf) } } } diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index 28b1ca5d16ab1..2001ee56287f5 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -1279,7 +1279,7 @@ extension ${Self} : RangeReplaceableCollection, _ArrayProtocol { /// Adds the elements of a sequence to the end of the array. /// - /// Use this method to append the elements of a sequence to the end of an + /// Use this method to append the elements of a sequence to the end of this /// array. This example appends the elements of a `Range` instance /// to an array of integers. /// @@ -1293,39 +1293,8 @@ extension ${Self} : RangeReplaceableCollection, _ArrayProtocol { /// - Complexity: O(*n*), where *n* is the length of the resulting array. public mutating func append(contentsOf newElements: S) where S.Iterator.Element == Element { - let oldCount = self.count - let capacity = self.capacity - let newCount = oldCount + newElements.underestimatedCount - if newCount > capacity { - self.reserveCapacity( - Swift.max(newCount, _growArrayCapacity(capacity))) - } - _buffer._arrayAppendSequence(newElements) - } - - // An overload of `append(contentsOf:)` that uses the += that is optimized for - // collections. - // FIXME(ABI)#13 (Performance): remove this entrypoint. The overload for `Sequence` should be - // made optimal for this case, too. - /// Adds the elements of a collection to the end of the array. - /// - /// Use this method to append the elements of a collection to the end of this - /// array. This example appends the elements of a `Range` instance - /// to an array of integers. - /// - /// var numbers = [1, 2, 3, 4, 5] - /// numbers.append(contentsOf: 10...15) - /// print(numbers) - /// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]" - /// - /// - Parameter newElements: The elements to append to the array. - /// - /// - Complexity: O(*n*), where *n* is the length of the resulting array. - public mutating func append(contentsOf newElements: C) - where C.Iterator.Element == Element { - - let newElementsCount = numericCast(newElements.count) as Int + let newElementsCount = newElements.underestimatedCount let oldCount = self.count let capacity = self.capacity @@ -1338,8 +1307,13 @@ extension ${Self} : RangeReplaceableCollection, _ArrayProtocol { Swift.max(newCount, _growArrayCapacity(capacity)) : newCount) - (self._buffer.firstElementAddress + oldCount).initialize(from: newElements) - self._buffer.count = newCount + let buf = UnsafeMutableBufferPointer(start: _buffer.firstElementAddress + oldCount, count: newElementsCount) + _buffer.count = newCount + if let remainder = buf.initialize(from: newElements) { + // there were elements that didn't fit in the existing buffer, + // append them in slow sequence-only mode + _buffer._arrayAppendSequence(IteratorSequence(remainder)) + } } %if Self == 'ArraySlice': @@ -1624,23 +1598,22 @@ extension ${Self} { return try body(&inoutBufferPointer) } - @discardableResult public func _copyContents( - initializing ptr: UnsafeMutablePointer - ) -> UnsafeMutablePointer { - if let s = self._baseAddressIfContiguous { - let count = self.count - ptr.initialize(from: s, count: count) + initializing buf: UnsafeMutableBufferPointer + ) -> Iterator? { + // FIXME: decide if this really is a fatalError + guard var p = buf.baseAddress + else { fatalError("Attempt to copy contents into nil buffer pointer") } + if let s = _baseAddressIfContiguous { + p.initialize(from: s, count: self.count) _fixLifetime(self._owner) - return ptr + count } else { - var p = ptr for x in self { p.initialize(to: x) p += 1 } - return p } + return nil } } %end @@ -1790,30 +1763,6 @@ public func += < >(lhs: inout ${Self}, rhs: S) { lhs.append(contentsOf: rhs) } - -// FIXME(ABI)#17 : remove this entrypoint. The functionality should be provided by -// a `+=` operator on `RangeReplaceableCollection`. -/// Appends the elements of a collection to ${a_Self}. -/// -/// Use this operator to append the elements of a collection to the end of -/// ${a_Self} with same `Element` type. This example appends -/// the elements of a `Range` instance to an array of integers. -/// -/// var numbers = [1, 2, 3, 4, 5] -/// numbers += 10...15 -/// print(numbers) -/// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]" -/// -/// - Parameters: -/// - lhs: The array to append to. -/// - rhs: A collection. -/// -/// - Complexity: O(*n*), where *n* is the length of the resulting array. -public func += < - C : Collection ->(lhs: inout ${Self}, rhs: C) { - lhs.append(contentsOf: rhs) -} % end //===--- generic helpers --------------------------------------------------===// diff --git a/stdlib/public/core/ContiguousArrayBuffer.swift b/stdlib/public/core/ContiguousArrayBuffer.swift index d60e7cbf87ecc..bffb112fca5c3 100644 --- a/stdlib/public/core/ContiguousArrayBuffer.swift +++ b/stdlib/public/core/ContiguousArrayBuffer.swift @@ -477,9 +477,11 @@ internal func += ( let oldCount = lhs.count let newCount = oldCount + numericCast(rhs.count) + let buf: UnsafeMutableBufferPointer + if _fastPath(newCount <= lhs.capacity) { + buf = UnsafeMutableBufferPointer(start: lhs.firstElementAddress + oldCount, count: numericCast(rhs.count)) lhs.count = newCount - (lhs.firstElementAddress + oldCount).initialize(from: rhs) } else { var newLHS = _ContiguousArrayBuffer( @@ -490,7 +492,13 @@ internal func += ( from: lhs.firstElementAddress, count: oldCount) lhs.count = 0 swap(&lhs, &newLHS) - (lhs.firstElementAddress + oldCount).initialize(from: rhs) + buf = UnsafeMutableBufferPointer(start: lhs.firstElementAddress + oldCount, count: numericCast(rhs.count)) + } + + if let remainder = buf.initialize(from: rhs) { + // there were elements that didn't fit in the existing buffer, + // append them in slow sequence-only mode + //FIXME: handle this possibility } } diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index 7bfc139c15e0d..d5ab34ddaaf35 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -192,8 +192,8 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - internal func __copyContents(initializing ptr: UnsafeMutablePointer) - -> UnsafeMutablePointer { + internal func __copyContents(initializing buf: UnsafeMutableBufferPointer) + -> AnyIterator? { _abstract() } @@ -387,9 +387,9 @@ internal final class _${Kind}Box : _Any${Kind}Box ContiguousArray { return _base._copyToContiguousArray() } - internal override func __copyContents(initializing ptr: UnsafeMutablePointer) - -> UnsafeMutablePointer { - return _base._copyContents(initializing: ptr) + internal override func __copyContents(initializing buf: UnsafeMutableBufferPointer) + -> AnyIterator? { + return _base._copyContents(initializing: buf).map(AnyIterator.init) } internal override func _drop( while predicate: (Element) throws -> Bool @@ -588,6 +588,8 @@ public struct AnySequence : Sequence { self.init(_ClosureBasedSequence(makeUnderlyingIterator)) } + public typealias Iterator = AnyIterator + internal init(_box: _AnySequenceBox) { self._box = _box } @@ -602,7 +604,7 @@ extension Any${Kind} { % else: /// Returns an iterator over the elements of this collection. % end - public func makeIterator() -> AnyIterator { + public func makeIterator() -> Iterator { return _box._makeIterator() } @@ -683,9 +685,9 @@ extension Any${Kind} { return self._box.__copyToContiguousArray() } - public func _copyContents(initializing ptr: UnsafeMutablePointer) - -> UnsafeMutablePointer { - return _box.__copyContents(initializing: ptr) + public func _copyContents(initializing buf: UnsafeMutableBufferPointer) + -> Iterator? { + return _box.__copyContents(initializing: buf).map(AnyIterator.init) } } % end @@ -808,6 +810,8 @@ public struct ${Self} // public typealias Indices // = Default${Traversal.replace('Forward', '')}Indices<${Self}> + public typealias Iterator = AnyIterator + internal init(_box: _${Self}Box) { self._box = _box } diff --git a/stdlib/public/core/LazyCollection.swift.gyb b/stdlib/public/core/LazyCollection.swift.gyb index 17ad43a238dc8..ef408264cbd60 100644 --- a/stdlib/public/core/LazyCollection.swift.gyb +++ b/stdlib/public/core/LazyCollection.swift.gyb @@ -75,11 +75,13 @@ public struct ${Self} : LazyCollectionProtocol { /// Forward implementations to the base collection, to pick up any /// optimizations it might implement. extension ${Self} : Sequence { + + public typealias Iterator = Base.Iterator /// Returns an iterator over the elements of this sequence. /// /// - Complexity: O(1). - public func makeIterator() -> Base.Iterator { + public func makeIterator() -> Iterator { return _base.makeIterator() } @@ -94,11 +96,10 @@ extension ${Self} : Sequence { return _base._copyToContiguousArray() } - @discardableResult public func _copyContents( - initializing ptr: UnsafeMutablePointer - ) -> UnsafeMutablePointer { - return _base._copyContents(initializing: ptr) + initializing buf: UnsafeMutableBufferPointer + ) -> Iterator? { + return _base._copyContents(initializing: buf) } public func _customContainsEquatableElement( diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index 866384af65a97..e2a34fa90a174 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -605,11 +605,11 @@ public protocol Sequence { /// in the same order. func _copyToContiguousArray() -> ContiguousArray - /// Copy a Sequence into an array, returning one past the last - /// element initialized. - @discardableResult - func _copyContents(initializing ptr: UnsafeMutablePointer) - -> UnsafeMutablePointer + /// Copy `self` into an unsafe buffer, returning a partially-consumed + /// iterator with any elements that didn't fit remaining. + func _copyContents( + initializing ptr: UnsafeMutableBufferPointer + ) -> Iterator? } /// A default makeIterator() function for `IteratorProtocol` instances that @@ -1317,19 +1317,23 @@ extension Sequence { } extension Sequence { - @discardableResult public func _copyContents( - initializing ptr: UnsafeMutablePointer - ) -> UnsafeMutablePointer { - var p = UnsafeMutablePointer(ptr) - for x in IteratorSequence(self.makeIterator()) { + initializing buf: UnsafeMutableBufferPointer + ) -> Iterator? { + var it = self.makeIterator() + // FIXME: decide if a nil buffer base pointer is valid or an error + guard let base = buf.baseAddress else { return it } + for p in base..<(base + buf.count) { + // FIXME: decide if underflow is an error that should trap + guard let x = it.next() else { break } p.initialize(to: x) - p += 1 } - return p + return it } } +// FIXME(ABI) #167(Recursive Protocol Constraints): IteratorSequence +// shouldn't be necessary, iterators should be sequences. // Pending and , // pass an IteratorProtocol through IteratorSequence to give it "Sequence-ness" /// A sequence built around an iterator of type `Base`. diff --git a/stdlib/public/core/SequenceWrapper.swift b/stdlib/public/core/SequenceWrapper.swift index e76169ea2cbcf..7e6627b14cdf6 100644 --- a/stdlib/public/core/SequenceWrapper.swift +++ b/stdlib/public/core/SequenceWrapper.swift @@ -119,8 +119,8 @@ extension Sequence /// element initialized. @discardableResult public func _copyContents( - initializing ptr: UnsafeMutablePointer - ) -> UnsafeMutablePointer { - return _base._copyContents(initializing: ptr) + initializing buf: UnsafeMutableBufferPointer + ) -> Base.Iterator? { + return _base._copyContents(initializing: buf) } } diff --git a/stdlib/public/core/UnsafeBufferPointer.swift.gyb b/stdlib/public/core/UnsafeBufferPointer.swift.gyb index 8cab8155833a3..8372b65c2c399 100644 --- a/stdlib/public/core/UnsafeBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeBufferPointer.swift.gyb @@ -224,6 +224,24 @@ extension Unsafe${Mutable}BufferPointer : CustomDebugStringConvertible { } %end + +extension UnsafeMutableBufferPointer { + /// Initializes memory in the buffer starting with the elements of `source`. + /// Returns an iterator to any elements of `source` that didn't fit in the + /// buffer. + /// + /// - Precondition: The memory at `self..(from source: S) -> S.Iterator? + where S.Iterator.Element == Iterator.Element { + return source._copyContents(initializing: self) + } +} + + @available(*, unavailable, renamed: "UnsafeBufferPointerIterator") public struct UnsafeBufferPointerGenerator {} diff --git a/stdlib/public/core/UnsafePointer.swift.gyb b/stdlib/public/core/UnsafePointer.swift.gyb index 839366200926d..39c3752df4d7e 100644 --- a/stdlib/public/core/UnsafePointer.swift.gyb +++ b/stdlib/public/core/UnsafePointer.swift.gyb @@ -311,9 +311,15 @@ public struct ${Self} /// /// - Postcondition: The `Pointee`s at `self..(from source: C) where C.Iterator.Element == Pointee { - source._copyContents(initializing: self) + let buf = UnsafeMutableBufferPointer(start: self, count: numericCast(source.count)) + if var remaining = source._copyContents(initializing: buf), + remaining.next() != nil { + fatalError("Would have overwritten memory with more elements than expected") + } } /// Replaces `count` initialized `Pointee`s starting at `self` with diff --git a/test/api-digester/source-stability.swift.expected b/test/api-digester/source-stability.swift.expected index 90b4cc8ca6d36..622b265e52c82 100644 --- a/test/api-digester/source-stability.swift.expected +++ b/test/api-digester/source-stability.swift.expected @@ -7,6 +7,9 @@ Protocol IndexableBase has been removed (deprecated) Protocol MutableIndexable has been removed (deprecated) Protocol RandomAccessIndexable has been removed (deprecated) Protocol RangeReplaceableIndexable has been removed (deprecated) +Func Array.append(contentsOf:) has been removed +Func ArraySlice.append(contentsOf:) has been removed +Func ContiguousArray.append(contentsOf:) has been removed ==================================================== Moved Decls ==================================================== // rdar://28456614 Swift 3 breaking change: static function 'abs' removed from Double/Float/Float80/CGFloat diff --git a/test/stdlib/UnsafeRawPointer.swift b/test/stdlib/UnsafeRawPointer.swift index c5ad8d7714f0e..b0b85a9a37600 100644 --- a/test/stdlib/UnsafeRawPointer.swift +++ b/test/stdlib/UnsafeRawPointer.swift @@ -62,7 +62,8 @@ UnsafeMutableRawPointerExtraTestSuite.test("bindMemory") { p1.deallocate(bytes: sizeInBytes, alignedTo: MemoryLayout.alignment) } let ptrI = p1.bindMemory(to: Int.self, capacity: 3) - ptrI.initialize(from: 1...3) + let bufI = UnsafeMutableBufferPointer(start: ptrI, count: 3) + bufI.initialize(from: 1...3) let ptrU = p1.bindMemory(to: UInt.self, capacity: 3) expectEqual(1, ptrU[0]) expectEqual(2, ptrU[1]) diff --git a/validation-test/stdlib/ExistentialCollection.swift.gyb b/validation-test/stdlib/ExistentialCollection.swift.gyb index bbabe95e3c2ad..2b00c59cc91b7 100644 --- a/validation-test/stdlib/ExistentialCollection.swift.gyb +++ b/validation-test/stdlib/ExistentialCollection.swift.gyb @@ -207,7 +207,7 @@ tests.test("${TestedType}: dispatch to wrapped, SequenceLog") { var result = Array(repeating: OpaqueValue(0), count: 10) Log._copyContents.expectIncrement(Base.self) { result.withUnsafeMutableBufferPointer { - _ = s._copyContents(initializing: $0.baseAddress!) + _ = s._copyContents(initializing: $0) } } } diff --git a/validation-test/stdlib/Lazy.swift.gyb b/validation-test/stdlib/Lazy.swift.gyb index d786babac8dcd..c9a178ff062e2 100644 --- a/validation-test/stdlib/Lazy.swift.gyb +++ b/validation-test/stdlib/Lazy.swift.gyb @@ -640,10 +640,10 @@ tests.test("LazySequence/Sequence") { func expectSequencePassthrough< S : Sequence, Base : Sequence - where - S : LazySequenceProtocol, Base : LoggingType, - Base.Iterator.Element == S.Iterator.Element ->(_ s: S, base: Base, arbitraryElement: S.Iterator.Element, count: Int) { +>(_ s: S, base: Base, arbitraryElement: S.Iterator.Element, count: Int) + where + S : LazySequenceProtocol, Base : LoggingType, + Base.Iterator.Element == S.Iterator.Element { let baseType = type(of: base) SequenceLog.makeIterator.expectIncrement(baseType) { _ = s.makeIterator() } @@ -658,12 +658,12 @@ func expectSequencePassthrough< } SequenceLog._copyContents.expectIncrement(baseType) { () -> Void in - let buf = UnsafeMutablePointer.allocate(capacity: count) - - let end = s._copyContents(initializing: buf) - expectTrue(end <= buf + count) - buf.deinitialize(count: end - buf) - buf.deallocate(capacity: count) + let ptr = UnsafeMutablePointer.allocate(capacity: count) + let buf = UnsafeMutableBufferPointer(start: ptr, count: count) + var remainders = s._copyContents(initializing: buf) + expectTrue(remainders == nil || remainders!.next() == nil) + ptr.deinitialize(count: count) + ptr.deallocate(capacity: count) } } From dbf4bf9f551628ee04e1c34ef6180de3ed500b5b Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 31 Oct 2016 15:21:10 -0700 Subject: [PATCH 002/153] Move += operator for append(contentsOf:) up to RangeReplaceableCollection --- stdlib/public/core/ArrayType.swift | 4 -- stdlib/public/core/Arrays.swift.gyb | 24 -------- .../core/RangeReplaceableCollection.swift.gyb | 55 +++++++++---------- test/Misc/misc_diagnostics.swift | 6 +- 4 files changed, 28 insertions(+), 61 deletions(-) diff --git a/stdlib/public/core/ArrayType.swift b/stdlib/public/core/ArrayType.swift index 0c15f3cc4f900..bc69ed2cf231c 100644 --- a/stdlib/public/core/ArrayType.swift +++ b/stdlib/public/core/ArrayType.swift @@ -43,10 +43,6 @@ internal protocol _ArrayProtocol /// - Complexity: O(`self.count`). mutating func reserveCapacity(_ minimumCapacity: Int) - /// Operator form of `append(contentsOf:)`. - static func += (lhs: inout Self, rhs: S) - where S.Iterator.Element == Iterator.Element - /// Insert `newElement` at index `i`. /// /// Invalidates all indices with respect to `self`. diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index 2001ee56287f5..a91f3769c8de5 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -1739,30 +1739,6 @@ extension ${Self} { } } } - -// FIXME(ABI)#16 : remove this entrypoint. The functionality should be provided by -// a `+=` operator on `RangeReplaceableCollection`. -/// Appends the elements of a sequence to ${a_Self}. -/// -/// Use this operator to append the elements of a sequence to the end of -/// ${a_Self} with same `Element` type. This example appends -/// the elements of a `Range` instance to an array of integers. -/// -/// var numbers = [1, 2, 3, 4, 5] -/// numbers += 10...15 -/// print(numbers) -/// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]" -/// -/// - Parameters: -/// - lhs: The array to append to. -/// - rhs: A collection or finite sequence. -/// -/// - Complexity: O(*n*), where *n* is the length of the resulting array. -public func += < - S : Sequence ->(lhs: inout ${Self}, rhs: S) { - lhs.append(contentsOf: rhs) -} % end //===--- generic helpers --------------------------------------------------===// diff --git a/stdlib/public/core/RangeReplaceableCollection.swift.gyb b/stdlib/public/core/RangeReplaceableCollection.swift.gyb index 0ab5393ab4ac7..e376f805e7e2a 100644 --- a/stdlib/public/core/RangeReplaceableCollection.swift.gyb +++ b/stdlib/public/core/RangeReplaceableCollection.swift.gyb @@ -294,26 +294,6 @@ public protocol RangeReplaceableCollection with newElements: C ) where C : Collection, C.Iterator.Element == Iterator.Element - /* - We could have these operators with default implementations, but the compiler - crashes: - - Dependent type should have been substituted by Sema - or SILGen - - func + (_: Self, _: S) -> Self - where S.Iterator.Element == Iterator.Element - - func + (_: S, _: Self) -> Self - where S.Iterator.Element == Iterator.Element - - func + (_: Self, _: S) -> Self - where S.Iterator.Element == Iterator.Element - - func + (_: Self, _: S) -> Self - where RC.Iterator.Element == Iterator.Element - */ - /// Prepares the collection to store the specified number of elements, when /// doing so is appropriate for the underlying type. /// @@ -370,16 +350,6 @@ public protocol RangeReplaceableCollection /// collection. mutating func append(_ newElement: Iterator.Element) - /* - The 'append(contentsOf:)' requirement should be an operator, but the compiler crashes: - - Dependent type should have been substituted by Sema - or SILGen - - func += (inout _: Self, _: S) - where S.Iterator.Element == Iterator.Element - */ - /// Adds the elements of a sequence or collection to the end of this /// collection. /// @@ -398,6 +368,8 @@ public protocol RangeReplaceableCollection /// /// - Complexity: O(*n*), where *n* is the length of the resulting /// collection. + // FIXME(ABI)#166 (Evolution): Consider replacing .append(contentsOf) with += + // suggestion in SE-91 mutating func append(contentsOf newElements: S) where S.Iterator.Element == Iterator.Element @@ -1171,6 +1143,29 @@ public func +< return lhs } +/// Appends the elements of a sequence to a range-replaceable collection. +/// +/// Use this operator to append the elements of a sequence to the end of +/// range-replaceable collection with same `Element` type. This example appends +/// the elements of a `Range` instance to an array of integers. +/// +/// var numbers = [1, 2, 3, 4, 5] +/// numbers += 10...15 +/// print(numbers) +/// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]" +/// +/// - Parameters: +/// - lhs: The array to append to. +/// - rhs: A collection or finite sequence. +/// +/// - Complexity: O(*n*), where *n* is the length of the resulting array. +public func += < + R : RangeReplaceableCollection, S : Sequence +>(lhs: inout R, rhs: S) + where R.Iterator.Element == S.Iterator.Element { + lhs.append(contentsOf: rhs) +} + @available(*, unavailable, renamed: "RangeReplaceableCollection") public typealias RangeReplaceableCollectionType = RangeReplaceableCollection diff --git a/test/Misc/misc_diagnostics.swift b/test/Misc/misc_diagnostics.swift index 9e34ef0772a52..56d9fd765d3bd 100644 --- a/test/Misc/misc_diagnostics.swift +++ b/test/Misc/misc_diagnostics.swift @@ -99,11 +99,11 @@ func test17875634() { var col = 2 var coord = (row, col) - match += (1, 2) // expected-error{{binary operator '+=' cannot be applied to operands of type '[(Int, Int)]' and '(Int, Int)'}} expected-note {{overloads for '+=' exist}} + match += (1, 2) // expected-error{{argument type '(Int, Int)' does not conform to expected type 'Sequence'}} - match += (row, col) // expected-error{{binary operator '+=' cannot be applied to operands of type '[(Int, Int)]' and '(Int, Int)'}} expected-note {{overloads for '+=' exist}} + match += (row, col) // expected-error{{argument type '(@lvalue Int, @lvalue Int)' does not conform to expected type 'Sequence'}} - match += coord // expected-error{{binary operator '+=' cannot be applied to operands of type '[(Int, Int)]' and '(Int, Int)'}} expected-note {{overloads for '+=' exist}} + match += coord // expected-error{{argument type '@lvalue (Int, Int)' does not conform to expected type 'Sequence'}} match.append(row, col) // expected-error{{extra argument in call}} From 3677d8e93f21d3267aa9de2bdd49992c1a427acc Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 31 Oct 2016 16:05:43 -0700 Subject: [PATCH 003/153] Added test of += matching .append(contentsOf:) to StdLibUnitTest --- .../CheckRangeReplaceableCollectionType.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/stdlib/private/StdlibCollectionUnittest/CheckRangeReplaceableCollectionType.swift b/stdlib/private/StdlibCollectionUnittest/CheckRangeReplaceableCollectionType.swift index 1d9dfb6e342da..a818be3b12be5 100644 --- a/stdlib/private/StdlibCollectionUnittest/CheckRangeReplaceableCollectionType.swift +++ b/stdlib/private/StdlibCollectionUnittest/CheckRangeReplaceableCollectionType.swift @@ -608,6 +608,19 @@ self.test("\(testNamePrefix).append(contentsOf:)/semantics") { } } +self.test("\(testNamePrefix).OperatorPlusEquals") { + for test in appendContentsOfTests { + var c = makeWrappedCollection(test.collection) + let newElements = + MinimalCollection(elements: test.newElements.map(wrapValue)) + c += newElements + expectEqualSequence( + test.expected, + c.map { extractValue($0).value }, + stackTrace: SourceLocStack().with(test.loc)) + } +} + //===----------------------------------------------------------------------===// // insert() //===----------------------------------------------------------------------===// From acfc7445f27fa49b5c83d10b47592b2551200afd Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 31 Oct 2016 17:13:00 -0700 Subject: [PATCH 004/153] Added test for undercounted collection not crashing --- validation-test/stdlib/Arrays.swift.gyb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/validation-test/stdlib/Arrays.swift.gyb b/validation-test/stdlib/Arrays.swift.gyb index 806da9c68f6b9..e491d2c6686ee 100644 --- a/validation-test/stdlib/Arrays.swift.gyb +++ b/validation-test/stdlib/Arrays.swift.gyb @@ -233,6 +233,15 @@ ArrayTestSuite.test("${array_type}/appendNonUnique") { } } +ArrayTestSuite.test("${array_type}/appendUndercountedCollection") { + // test that the array safely handles a + // collection that understates its count + var i = 0 + let l = repeatElement(42, count: 10_000).lazy.filter { _ in i += 1; return i > 10_000 } + var a: ${array_type} = [] + a.append(contentsOf: l) +} + ArrayTestSuite.test("${array_type}/emptyAllocation") { let arr0 = ${array_type}() let arr1 = ${array_type}(repeating: LifetimeTracked(0), count: 0) From c857dabd7cd340917f43efd9132f3d1320eae032 Mon Sep 17 00:00:00 2001 From: ben-cohen Date: Fri, 4 Nov 2016 11:44:28 -0700 Subject: [PATCH 005/153] Switch to _copyContents returning an (Iterator,UnsafeBuffer.Index) --- .../CheckSequenceInstance.swift.gyb | 6 ++- .../LoggingWrappers.swift.gyb | 7 +-- stdlib/public/SDK/Foundation/Data.swift | 5 +- stdlib/public/core/Arrays.swift.gyb | 51 ++++++++++++++----- .../public/core/ContiguousArrayBuffer.swift | 10 ++-- .../core/ExistentialCollection.swift.gyb | 12 +++-- stdlib/public/core/LazyCollection.swift.gyb | 2 +- stdlib/public/core/Sequence.swift | 25 ++++++--- stdlib/public/core/SequenceWrapper.swift | 2 +- .../public/core/UnsafeBufferPointer.swift.gyb | 14 ++--- stdlib/public/core/UnsafePointer.swift.gyb | 8 +-- validation-test/stdlib/Arrays.swift.gyb | 5 +- validation-test/stdlib/Lazy.swift.gyb | 7 ++- 13 files changed, 101 insertions(+), 53 deletions(-) diff --git a/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb b/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb index f5e4b7c7c04a5..69a780cf52c69 100644 --- a/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb +++ b/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb @@ -88,9 +88,11 @@ public func checkSequence< for _ in sequence { count += 1 } let ptr = UnsafeMutablePointer.allocate(capacity: count) let buf = UnsafeMutableBufferPointer(start: ptr, count: count) - var remainders = sequence._copyContents(initializing: buf) - expectTrue(remainders == nil || remainders!.next() == nil, + var (remainders,writtenUpTo) = sequence._copyContents(initializing: buf) + expectTrue(remainders.next() == nil, "_copyContents returned unwritten elements") + expectTrue(writtenUpTo == buf.endIndex, + "_copyContents failed to use entire buffer") expectEqualSequence(expected, buf, ${trace}, sameValue: sameValue) ptr.deinitialize(count: count) ptr.deallocate(capacity: count) diff --git a/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb b/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb index 25507e8c791e1..96c0ac9237dd3 100644 --- a/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb +++ b/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb @@ -321,10 +321,11 @@ public struct ${Self}< /// Copy a Sequence into an array. public func _copyContents( - initializing buf: UnsafeMutableBufferPointer - ) -> Iterator? { + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { Log._copyContents[selfType] += 1 - return base._copyContents(initializing: buf).map(Iterator.init) + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) } % if Kind in ['Collection', 'MutableCollection', 'RangeReplaceableCollection']: diff --git a/stdlib/public/SDK/Foundation/Data.swift b/stdlib/public/SDK/Foundation/Data.swift index 10a8da32afcd7..caf0e0f77543e 100644 --- a/stdlib/public/SDK/Foundation/Data.swift +++ b/stdlib/public/SDK/Foundation/Data.swift @@ -552,8 +552,9 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl if replacementCount != 0 { let buf = UnsafeMutableBufferPointer(start: bytes + start, count: numericCast(newElements.count)) - // FIXME: is this guaranteed to return no residual elements? - let _ = newElements._copyContents(initializing: buf) + var (it,idx) = newElements._copyContents(initializing: buf) + precondition(it.next() == nil && idx == buf.endIndex, + "newElements iterator returned different count to newElements.count") } } } diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index a91f3769c8de5..d98f55d66c801 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -1297,20 +1297,33 @@ extension ${Self} : RangeReplaceableCollection, _ArrayProtocol { let newElementsCount = newElements.underestimatedCount let oldCount = self.count - let capacity = self.capacity + let oldCapacity = self.capacity let newCount = oldCount + newElementsCount // Ensure uniqueness, mutability, and sufficient storage. Note that // for consistency, we need unique self even if newElements is empty. self.reserveCapacity( - newCount > capacity ? - Swift.max(newCount, _growArrayCapacity(capacity)) + newCount > oldCapacity ? + Swift.max(newCount, _growArrayCapacity(oldCapacity)) : newCount) - let buf = UnsafeMutableBufferPointer(start: _buffer.firstElementAddress + oldCount, count: newElementsCount) - _buffer.count = newCount - if let remainder = buf.initialize(from: newElements) { - // there were elements that didn't fit in the existing buffer, + let startNewElements = _buffer.firstElementAddress + oldCount + let buf = UnsafeMutableBufferPointer( + start: startNewElements, + count: self.capacity - oldCount) + + let (remainder,writtenUpTo) = buf.initialize(from: newElements) + + // trap on underflow from the sequence's underestimate: + let writtenCount = buf.distance(from: buf.startIndex, to: writtenUpTo) + _precondition(newElementsCount <= writtenCount, + "newElements.underestimatedCount was an overestimate") + // can't check for overflow as sequences can underestimate + + _buffer.count += writtenCount + + if writtenUpTo == buf.endIndex { + // there may be elements that didn't fit in the existing buffer, // append them in slow sequence-only mode _buffer._arrayAppendSequence(IteratorSequence(remainder)) } @@ -1599,13 +1612,22 @@ extension ${Self} { } public func _copyContents( - initializing buf: UnsafeMutableBufferPointer - ) -> Iterator? { - // FIXME: decide if this really is a fatalError - guard var p = buf.baseAddress - else { fatalError("Attempt to copy contents into nil buffer pointer") } + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + + guard !self.isEmpty else { return (makeIterator(),buffer.startIndex) } + + // It is not OK for there to be no pointer/not enough space, as this is + // a precondition and Array never lies about its count. + guard var p = buffer.baseAddress + else { _preconditionFailure("Attempt to copy contents into nil buffer pointer") } + _precondition(self.count <= buffer.count, + "Insufficient space allocated to copy array contents") + if let s = _baseAddressIfContiguous { p.initialize(from: s, count: self.count) + // Need a _fixLifetime bracketing the _baseAddressIfContiguous getter + // and all uses of the pointer it returns: _fixLifetime(self._owner) } else { for x in self { @@ -1613,7 +1635,10 @@ extension ${Self} { p += 1 } } - return nil + + var it = IndexingIterator(_elements: self) + it._position = endIndex + return (it,buffer.index(buffer.startIndex, offsetBy: self.count)) } } %end diff --git a/stdlib/public/core/ContiguousArrayBuffer.swift b/stdlib/public/core/ContiguousArrayBuffer.swift index bffb112fca5c3..1ad44fa8b0620 100644 --- a/stdlib/public/core/ContiguousArrayBuffer.swift +++ b/stdlib/public/core/ContiguousArrayBuffer.swift @@ -495,11 +495,11 @@ internal func += ( buf = UnsafeMutableBufferPointer(start: lhs.firstElementAddress + oldCount, count: numericCast(rhs.count)) } - if let remainder = buf.initialize(from: rhs) { - // there were elements that didn't fit in the existing buffer, - // append them in slow sequence-only mode - //FIXME: handle this possibility - } + var (remainders,writtenUpTo) = buf.initialize(from: rhs) + + // ensure that exactly rhs.count elements were written + _precondition(remainders.next() == nil, "rhs underreported its count") + _precondition(writtenUpTo == buf.endIndex, "rhs overreported its count") } extension _ContiguousArrayBuffer : RandomAccessCollection { diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index d5ab34ddaaf35..05bfdd5838fa7 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -193,7 +193,7 @@ internal class _AnyRandomAccessCollectionBox } internal func __copyContents(initializing buf: UnsafeMutableBufferPointer) - -> AnyIterator? { + -> (AnyIterator,UnsafeMutableBufferPointer.Index) { _abstract() } @@ -388,8 +388,9 @@ internal final class _${Kind}Box : _Any${Kind}Box) - -> AnyIterator? { - return _base._copyContents(initializing: buf).map(AnyIterator.init) + -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _base._copyContents(initializing: buf) + return (AnyIterator(it),idx) } internal override func _drop( while predicate: (Element) throws -> Bool @@ -686,8 +687,9 @@ extension Any${Kind} { } public func _copyContents(initializing buf: UnsafeMutableBufferPointer) - -> Iterator? { - return _box.__copyContents(initializing: buf).map(AnyIterator.init) + -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _box.__copyContents(initializing: buf) + return (AnyIterator(it),idx) } } % end diff --git a/stdlib/public/core/LazyCollection.swift.gyb b/stdlib/public/core/LazyCollection.swift.gyb index ef408264cbd60..62df72524177a 100644 --- a/stdlib/public/core/LazyCollection.swift.gyb +++ b/stdlib/public/core/LazyCollection.swift.gyb @@ -98,7 +98,7 @@ extension ${Self} : Sequence { public func _copyContents( initializing buf: UnsafeMutableBufferPointer - ) -> Iterator? { + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { return _base._copyContents(initializing: buf) } diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index e2a34fa90a174..fa44fddb542f3 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -609,7 +609,7 @@ public protocol Sequence { /// iterator with any elements that didn't fit remaining. func _copyContents( initializing ptr: UnsafeMutableBufferPointer - ) -> Iterator? + ) -> (Iterator,UnsafeMutableBufferPointer.Index) } /// A default makeIterator() function for `IteratorProtocol` instances that @@ -1317,18 +1317,27 @@ extension Sequence { } extension Sequence { + /// Copies `self` into the supplied buffer. + /// + /// - Precondition: The memory in `self` is uninitialized. The buffer must + /// contain sufficient uninitialized memory to accommodate `source.underestimatedCount`. + /// + /// - Postcondition: The `Pointee`s at `buffer[startIndex.. - ) -> Iterator? { + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { var it = self.makeIterator() - // FIXME: decide if a nil buffer base pointer is valid or an error - guard let base = buf.baseAddress else { return it } - for p in base..<(base + buf.count) { - // FIXME: decide if underflow is an error that should trap + guard let base = buffer.baseAddress else { return (it,buffer.startIndex) } + var idx = buffer.startIndex + for p in base..<(base + buffer.count) { + // underflow is permitted – e.g. a sequence into + // the spare capacity of an Array buffer guard let x = it.next() else { break } p.initialize(to: x) + buffer.formIndex(after: &idx) } - return it + return (it,idx) } } diff --git a/stdlib/public/core/SequenceWrapper.swift b/stdlib/public/core/SequenceWrapper.swift index 7e6627b14cdf6..a87459dcc5e57 100644 --- a/stdlib/public/core/SequenceWrapper.swift +++ b/stdlib/public/core/SequenceWrapper.swift @@ -120,7 +120,7 @@ extension Sequence @discardableResult public func _copyContents( initializing buf: UnsafeMutableBufferPointer - ) -> Base.Iterator? { + ) -> (Base.Iterator,UnsafeMutableBufferPointer.Index) { return _base._copyContents(initializing: buf) } } diff --git a/stdlib/public/core/UnsafeBufferPointer.swift.gyb b/stdlib/public/core/UnsafeBufferPointer.swift.gyb index 8372b65c2c399..cebe768667167 100644 --- a/stdlib/public/core/UnsafeBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeBufferPointer.swift.gyb @@ -226,16 +226,18 @@ extension Unsafe${Mutable}BufferPointer : CustomDebugStringConvertible { extension UnsafeMutableBufferPointer { - /// Initializes memory in the buffer starting with the elements of `source`. + /// Initializes memory in the buffer with the elements of `source`. /// Returns an iterator to any elements of `source` that didn't fit in the - /// buffer. + /// buffer, and an index to the point in the buffer one past the last element + /// written (so `startIndex` if no elements written, `endIndex` if the buffer + /// was completely filled). /// - /// - Precondition: The memory at `self..(from source: S) -> S.Iterator? + public func initialize(from source: S) -> (S.Iterator, Index) where S.Iterator.Element == Iterator.Element { return source._copyContents(initializing: self) } diff --git a/stdlib/public/core/UnsafePointer.swift.gyb b/stdlib/public/core/UnsafePointer.swift.gyb index 39c3752df4d7e..79999b619d57b 100644 --- a/stdlib/public/core/UnsafePointer.swift.gyb +++ b/stdlib/public/core/UnsafePointer.swift.gyb @@ -316,10 +316,10 @@ public struct ${Self} public func initialize(from source: C) where C.Iterator.Element == Pointee { let buf = UnsafeMutableBufferPointer(start: self, count: numericCast(source.count)) - if var remaining = source._copyContents(initializing: buf), - remaining.next() != nil { - fatalError("Would have overwritten memory with more elements than expected") - } + var (remainders,writtenUpTo) = source._copyContents(initializing: buf) + // ensure that exactly rhs.count elements were written + _precondition(remainders.next() == nil, "rhs underreported its count") + _precondition(writtenUpTo == buf.endIndex, "rhs overreported its count") } /// Replaces `count` initialized `Pointee`s starting at `self` with diff --git a/validation-test/stdlib/Arrays.swift.gyb b/validation-test/stdlib/Arrays.swift.gyb index e491d2c6686ee..fbf79fb443013 100644 --- a/validation-test/stdlib/Arrays.swift.gyb +++ b/validation-test/stdlib/Arrays.swift.gyb @@ -237,7 +237,10 @@ ArrayTestSuite.test("${array_type}/appendUndercountedCollection") { // test that the array safely handles a // collection that understates its count var i = 0 - let l = repeatElement(42, count: 10_000).lazy.filter { _ in i += 1; return i > 10_000 } + let l = repeatElement(42, count: 10_000).lazy + // capture i by reference and change behavior + // between first pass (for count) and second + .filter { _ in i += 1; return i > 10_000 } var a: ${array_type} = [] a.append(contentsOf: l) } diff --git a/validation-test/stdlib/Lazy.swift.gyb b/validation-test/stdlib/Lazy.swift.gyb index c9a178ff062e2..1e1e632f74480 100644 --- a/validation-test/stdlib/Lazy.swift.gyb +++ b/validation-test/stdlib/Lazy.swift.gyb @@ -660,8 +660,11 @@ func expectSequencePassthrough< SequenceLog._copyContents.expectIncrement(baseType) { () -> Void in let ptr = UnsafeMutablePointer.allocate(capacity: count) let buf = UnsafeMutableBufferPointer(start: ptr, count: count) - var remainders = s._copyContents(initializing: buf) - expectTrue(remainders == nil || remainders!.next() == nil) + var (remainders,writtenUpTo) = s._copyContents(initializing: buf) + expectTrue(remainders.next() == nil, + "_copyContents returned unwritten elements") + expectTrue(writtenUpTo == buf.endIndex, + "_copyContents failed to use entire buffer") ptr.deinitialize(count: count) ptr.deallocate(capacity: count) } From 3074318e453f29201b14a80965e18c6e8abdce57 Mon Sep 17 00:00:00 2001 From: ben-cohen Date: Sun, 18 Dec 2016 07:33:47 -0800 Subject: [PATCH 006/153] perf improvement to Sequence._copyContents --- stdlib/public/core/Sequence.swift | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index fa44fddb542f3..7273fd14b55f0 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -1327,18 +1327,18 @@ extension Sequence { public func _copyContents( initializing buffer: UnsafeMutableBufferPointer ) -> (Iterator,UnsafeMutableBufferPointer.Index) { - var it = self.makeIterator() - guard let base = buffer.baseAddress else { return (it,buffer.startIndex) } - var idx = buffer.startIndex - for p in base..<(base + buffer.count) { - // underflow is permitted – e.g. a sequence into - // the spare capacity of an Array buffer - guard let x = it.next() else { break } - p.initialize(to: x) - buffer.formIndex(after: &idx) + public func _copyContents( + var it = self.makeIterator() + guard var ptr = buffer.baseAddress else { return (it,buffer.startIndex) } + for idx in buffer.startIndex.. Date: Sun, 18 Dec 2016 10:53:02 -0800 Subject: [PATCH 007/153] Fix accidental miscommit --- stdlib/public/core/Sequence.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index 50dc0d5c4276c..b39cdc896f9ba 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -1379,7 +1379,6 @@ extension Sequence { public func _copyContents( initializing buffer: UnsafeMutableBufferPointer ) -> (Iterator,UnsafeMutableBufferPointer.Index) { - public func _copyContents( var it = self.makeIterator() guard var ptr = buffer.baseAddress else { return (it,buffer.startIndex) } for idx in buffer.startIndex.. Date: Sun, 18 Dec 2016 08:21:46 -0800 Subject: [PATCH 008/153] add tests of generic append --- benchmark/single-source/ArrayAppend.swift | 83 +++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/benchmark/single-source/ArrayAppend.swift b/benchmark/single-source/ArrayAppend.swift index 58284d90a4806..fa2d38d84ca1b 100644 --- a/benchmark/single-source/ArrayAppend.swift +++ b/benchmark/single-source/ArrayAppend.swift @@ -60,6 +60,21 @@ public func run_ArrayAppendSequence(_ N: Int) { // can pre-reserve capacity. @inline(never) public func run_ArrayAppendArrayOfInt(_ N: Int) { + let other = Array(repeating: 1, count: 10_000) + for _ in 0..(array: inout [S.Iterator.Element], sequence: S) { + array.append(contentsOf: sequence) +} + +@inline(never) +public func run_ArrayAppendFromGeneric(_ N: Int) { + let other = Array(repeating: 1, count: 10_000) + + for _ in 0..(collection: inout R, array: [R.Iterator.Element]) { + collection.append(contentsOf: array) +} + +@inline(never) +public func run_ArrayAppendToGeneric(_ N: Int) { + let other = Array(repeating: 1, count: 10_000) + + for _ in 0..(collection: inout R, sequence: S) +where R.Iterator.Element == S.Iterator.Element { + collection.append(contentsOf: sequence) +} + +@inline(never) +public func run_ArrayAppendToFromGeneric(_ N: Int) { + let other = Array(repeating: 1, count: 10_000) + + for _ in 0.. Date: Mon, 19 Dec 2016 08:55:29 -0800 Subject: [PATCH 009/153] Merge pull request #6377 from airspeedswift/add-append-tests [benchmarks] Add new array append tests to benchmark main.swift --- benchmark/utils/main.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/benchmark/utils/main.swift b/benchmark/utils/main.swift index 054aa2d051119..d6e464ba58a2a 100644 --- a/benchmark/utils/main.swift +++ b/benchmark/utils/main.swift @@ -112,11 +112,15 @@ precommitTests = [ "ArrayAppendReserved": run_ArrayAppendReserved, "ArrayAppendSequence": run_ArrayAppendSequence, "ArrayAppendArrayOfInt": run_ArrayAppendArrayOfInt, + "ArrayPlusEqualArrayOfInt": run_ArrayPlusEqualArrayOfInt, "ArrayAppendStrings": run_ArrayAppendStrings, "ArrayAppendGenericStructs": run_ArrayAppendGenericStructs, "ArrayAppendOptionals": run_ArrayAppendOptionals, "ArrayAppendLazyMap": run_ArrayAppendLazyMap, "ArrayAppendRepeatCol": run_ArrayAppendRepeatCol, + "ArrayAppendToGeneric": run_ArrayAppendToGeneric, + "ArrayAppendFromGeneric": run_ArrayAppendFromGeneric, + "ArrayAppendToFromGeneric": run_ArrayAppendToFromGeneric, "ArrayInClass": run_ArrayInClass, "ArrayLiteral": run_ArrayLiteral, "ArrayOfGenericPOD": run_ArrayOfGenericPOD, From fdd02f61d31b802493df7bffd52dd1d525e53fc4 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Wed, 21 Dec 2016 19:40:24 -0800 Subject: [PATCH 010/153] [CMake] Stop linking CoreFoundation into swiftc and libobjc into everything. We link the stdlib against Foundation and that's quite sufficient for the REPL and script mode to work. --- cmake/modules/AddSwift.cmake | 2 -- tools/driver/CMakeLists.txt | 15 --------------- 2 files changed, 17 deletions(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 30014c3269a56..688e6eca389d5 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -341,8 +341,6 @@ function(_add_variant_link_flags) list(APPEND library_search_directories "${SWIFT_ANDROID_PREBUILT_PATH}/lib/gcc/arm-linux-androideabi/${SWIFT_ANDROID_NDK_GCC_VERSION}.x") else() - list(APPEND result "-lobjc") - # If lto is enabled, we need to add the object path flag so that the LTO code # generator leaves the intermediate object file in a place where it will not # be touched. The reason why this must be done is that on OS X, debug info is diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 902b9fe97c4f0..4586492e80e6f 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -31,21 +31,6 @@ if(NOT SWIFT_BUILT_STANDALONE) add_dependencies(swift clang-headers) endif() -# Platforms that have a REPL need extra libraries to be linked into the 'swift' -# binary. -include(SwiftDarwin) -if(SWIFT_HOST_VARIANT MATCHES "${SWIFT_DARWIN_VARIANTS}") - # Link in CoreFoundation on Darwin. - find_library(CORE_FOUNDATION NAMES CoreFoundation) - target_link_libraries(swift ${CORE_FOUNDATION}) - - # Link in arclite on Darwin. - get_default_toolchain_dir(toolchain_dir) - set(SWIFT_REPL_ARCLITE "${toolchain_dir}/usr/lib/arc/libarclite_${SWIFT_HOST_VARIANT}.a") - set_property(TARGET swift APPEND_STRING PROPERTY - LINK_FLAGS " -lobjc -Wl,-force_load,\"${SWIFT_REPL_ARCLITE}\"") -endif() - swift_install_in_component(compiler FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swiftc" DESTINATION "bin") From 0936c51a967e1f29c2f96cacc64737a33e5b6aa8 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 22 Dec 2016 21:52:29 +0100 Subject: [PATCH 011/153] [swiftc (83 vs. 5325)] Add crasher in swift::TypeChecker::checkAutoClosureAttr Add test case for crash triggered in `swift::TypeChecker::checkAutoClosureAttr`. Current number of unresolved compiler crashers: 83 (5325 resolved) /cc @slavapestov - just wanted to let you know that this crasher caused an assertion failure for the assertion `!typeInContext.isNull() && "no contextual type set yet"` added on 2016-12-15 by you in commit 757f253a3 :-) Assertion failure in [`include/swift/AST/Decl.h (line 4121)`](https://github.com/apple/swift/blob/master/include/swift/AST/Decl.h#L4121): ``` Assertion `!typeInContext.isNull() && "no contextual type set yet"' failed. When executing: swift::Type swift::VarDecl::getType() const ``` Assertion context: ``` } /// Get the type of the variable within its context. If the context is generic, /// this will use archetypes. Type getType() const { assert(!typeInContext.isNull() && "no contextual type set yet"); return typeInContext; } /// Set the type of the variable within its context. void setType(Type t); ``` Stack trace: ``` 0 0x0000000003500d48 llvm::sys::PrintStackTrace(llvm::raw_ostream&) (/path/to/swift/bin/swift+0x3500d48) 1 0x0000000003501486 SignalHandler(int) (/path/to/swift/bin/swift+0x3501486) 2 0x00007f6d5d5143e0 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x113e0) 3 0x00007f6d5bc42428 gsignal /build/glibc-Qz8a69/glibc-2.23/signal/../sysdeps/unix/sysv/linux/raise.c:54:0 4 0x00007f6d5bc4402a abort /build/glibc-Qz8a69/glibc-2.23/stdlib/abort.c:91:0 5 0x00007f6d5bc3abd7 __assert_fail_base /build/glibc-Qz8a69/glibc-2.23/assert/assert.c:92:0 6 0x00007f6d5bc3ac82 (/lib/x86_64-linux-gnu/libc.so.6+0x2dc82) 7 0x0000000000ce3665 swift::TypeChecker::checkAutoClosureAttr(swift::ParamDecl*, swift::AutoClosureAttr*) (/path/to/swift/bin/swift+0xce3665) 8 0x0000000000ce2e47 swift::TypeChecker::checkTypeModifyingDeclAttributes(swift::VarDecl*) (/path/to/swift/bin/swift+0xce2e47) 9 0x0000000000d325ba swift::TypeChecker::typeCheckParameterList(swift::ParameterList*, swift::DeclContext*, swift::OptionSet, swift::GenericTypeResolver&) (/path/to/swift/bin/swift+0xd325ba) 10 0x0000000000d2c43a checkGenericFuncSignature(swift::TypeChecker&, swift::ArchetypeBuilder*, swift::AbstractFunctionDecl*, swift::GenericTypeResolver&) (/path/to/swift/bin/swift+0xd2c43a) 11 0x0000000000d2b79e swift::TypeChecker::validateGenericFuncSignature(swift::AbstractFunctionDecl*) (/path/to/swift/bin/swift+0xd2b79e) 12 0x0000000000d1615b (anonymous namespace)::DeclChecker::visitFuncDecl(swift::FuncDecl*) (/path/to/swift/bin/swift+0xd1615b) 13 0x0000000000d05c00 (anonymous namespace)::DeclChecker::visit(swift::Decl*) (/path/to/swift/bin/swift+0xd05c00) 14 0x0000000000d13bbb (anonymous namespace)::DeclChecker::visitProtocolDecl(swift::ProtocolDecl*) (/path/to/swift/bin/swift+0xd13bbb) 15 0x0000000000d05be0 (anonymous namespace)::DeclChecker::visit(swift::Decl*) (/path/to/swift/bin/swift+0xd05be0) 16 0x0000000000d059c3 swift::TypeChecker::typeCheckDecl(swift::Decl*, bool) (/path/to/swift/bin/swift+0xd059c3) 17 0x0000000000c1e1b5 swift::performTypeChecking(swift::SourceFile&, swift::TopLevelContext&, swift::OptionSet, unsigned int, unsigned int) (/path/to/swift/bin/swift+0xc1e1b5) 18 0x0000000000992086 swift::CompilerInstance::performSema() (/path/to/swift/bin/swift+0x992086) 19 0x000000000047c5aa swift::performFrontend(llvm::ArrayRef, char const*, void*, swift::FrontendObserver*) (/path/to/swift/bin/swift+0x47c5aa) 20 0x000000000043ade7 main (/path/to/swift/bin/swift+0x43ade7) 21 0x00007f6d5bc2d830 __libc_start_main /build/glibc-Qz8a69/glibc-2.23/csu/../csu/libc-start.c:325:0 22 0x0000000000438229 _start (/path/to/swift/bin/swift+0x438229) ``` --- ...peincontext-isnull-no-contextual-type-set-yet.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 validation-test/compiler_crashers/28595-typeincontext-isnull-no-contextual-type-set-yet.swift diff --git a/validation-test/compiler_crashers/28595-typeincontext-isnull-no-contextual-type-set-yet.swift b/validation-test/compiler_crashers/28595-typeincontext-isnull-no-contextual-type-set-yet.swift new file mode 100644 index 0000000000000..4ae9d86bac3f8 --- /dev/null +++ b/validation-test/compiler_crashers/28595-typeincontext-isnull-no-contextual-type-set-yet.swift @@ -0,0 +1,10 @@ +// This source file is part of the Swift.org open source project +// Copyright (c) 2014 - 2016 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: not --crash %target-swift-frontend %s -emit-ir +// REQUIRES: asserts +protocol b{func a(@autoclosure()) From 1889fde2284916e2c368c9c7cc87906adae9155b Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 19 Dec 2016 17:01:17 -0800 Subject: [PATCH 012/153] Resolve `type(of:)` by overload resolution rather than parse hackery. `type(of:)` has behavior whose type isn't directly representable in Swift's type system, since it produces both concrete and existential metatypes. In Swift 3 we put in a parser hack to turn `type(of: )` into a DynamicTypeExpr, but this effectively made `type(of:)` a reserved name. It's a bit more principled to put `Swift.type(of:)` on the same level as other declarations, even with its special-case type system behavior, and we can do this by special-casing the type system we produce during overload resolution if `Swift.type(of:)` shows up in an overload set. This also lays groundwork for handling other declarations we want to ostensibly behave like normal declarations but with otherwise inexpressible types, viz. `withoutActuallyEscaping` from SE-0110. --- include/swift/AST/DiagnosticsSema.def | 3 ++ lib/Parse/ParseExpr.cpp | 5 +++ lib/Sema/CSApply.cpp | 56 +++++++++++++++++++++++---- lib/Sema/ConstraintSystem.cpp | 51 ++++++++++++++++++++++-- lib/Sema/MiscDiagnostics.cpp | 14 +++++++ lib/Sema/OverloadChoice.h | 2 +- lib/Sema/TypeChecker.cpp | 10 +++++ lib/Sema/TypeChecker.h | 14 +++++++ stdlib/public/core/Builtin.swift | 51 ++++++++++++++++++++++++ test/Constraints/type_of.swift | 38 ++++++++++++++++++ test/Parse/type_expr.swift | 6 +-- 11 files changed, 236 insertions(+), 14 deletions(-) create mode 100644 test/Constraints/type_of.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 403f1aaaa07e6..e5ed60b6989fc 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2432,6 +2432,9 @@ NOTE(fix_unqualified_access_top_level_multi,none, "use '%0' to reference the %1 in module %2", (StringRef, DescriptiveDeclKind, Identifier)) +ERROR(unsupported_special_decl_ref, none, + "referencing %0 as a function value is not implemented", (Identifier)) + WARNING(use_of_qq_on_non_optional_value,none, "left side of nil coalescing operator '?""?' has non-optional type %0, " "so the right side is never used", (Type)) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index f6da981bde7ac..efdab6aa6f4b6 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -968,6 +968,11 @@ getMagicIdentifierLiteralKind(tok Kind) { /// See if type(of: ) can be parsed backtracking on failure. static bool canParseTypeOf(Parser &P) { + // We parsed `type(of:)` as a special syntactic form in Swift 3. In Swift 4 + // it is handled by overload resolution. + if (!P.Context.LangOpts.isSwiftVersion3()) + return false; + if (!(P.Tok.getText() == "type" && P.peekToken().is(tok::l_paren))) { return false; } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 630bf92074cda..47bf39f0a7198 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -387,7 +387,7 @@ namespace { AccessSemantics semantics) { // Determine the declaration selected for this overloaded reference. auto &ctx = cs.getASTContext(); - + // If this is a member of a nominal type, build a reference to the // member with an implied base type. if (decl->getDeclContext()->isTypeContext() && isa(decl)) { @@ -5230,6 +5230,16 @@ ClosureExpr *ExprRewriter::coerceClosureExprFromNever(ClosureExpr *closureExpr) return closureExpr; } +// Look through sugar and DotSyntaxBaseIgnoredExprs. +static Expr * +getSemanticExprForDeclOrMemberRef(Expr *expr) { + auto semanticExpr = expr->getSemanticsProvidingExpr(); + while (auto ignoredBase = dyn_cast(semanticExpr)){ + semanticExpr = ignoredBase->getRHS()->getSemanticsProvidingExpr(); + } + return semanticExpr; +} + static void maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr, AnyFunctionType *toType) { @@ -5252,11 +5262,7 @@ maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr, // Can convert a decl ref to a global or local function that doesn't // capture context. Look through ignored bases too. // TODO: Look through static method applications to the type. - auto semanticExpr = expr->getSemanticsProvidingExpr(); - while (auto ignoredBase = dyn_cast(semanticExpr)){ - semanticExpr = ignoredBase->getRHS()->getSemanticsProvidingExpr(); - } - + auto semanticExpr = getSemanticExprForDeclOrMemberRef(expr); auto maybeDiagnoseFunctionRef = [&](FuncDecl *fn) { // TODO: We could allow static (or class final) functions too by // "capturing" the metatype in a thunk. @@ -6330,13 +6336,49 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, TypeChecker &tc = cs.getTypeChecker(); auto fn = apply->getFn(); - + + auto finishApplyOfDeclWithSpecialTypeCheckingSemantics + = [&](ApplyExpr *apply, + ValueDecl *decl, + Type openedType) -> Expr* { + switch (cs.TC.getDeclTypeCheckingSemantics(decl)) { + case DeclTypeCheckingSemantics::TypeOf: { + // Resolve into a DynamicTypeExpr. + auto arg = apply->getArg(); + if (auto tuple = dyn_cast(arg)) + arg = tuple->getElements()[0]; + auto replacement = new (cs.getASTContext()) + DynamicTypeExpr(apply->getFn()->getLoc(), + apply->getArg()->getStartLoc(), + arg, + apply->getArg()->getEndLoc(), + Type()); + cs.setType(replacement, simplifyType(openedType)); + return replacement; + } + + case DeclTypeCheckingSemantics::Normal: + return nullptr; + } + }; + // The function is always an rvalue. fn = cs.coerceToRValue(fn); assert(fn && "Rvalue conversion failed?"); if (!fn) return nullptr; + // Resolve applications of decls with special semantics. + if (auto declRef = + dyn_cast(getSemanticExprForDeclOrMemberRef(fn))) { + if (auto special = + finishApplyOfDeclWithSpecialTypeCheckingSemantics(apply, + declRef->getDecl(), + openedType)) { + return special; + } + } + // Handle applications that look through ImplicitlyUnwrappedOptional. if (auto fnTy = cs.lookThroughImplicitlyUnwrappedOptionalType(cs.getType(fn))) fn = coerceImplicitlyUnwrappedOptionalToValue(fn, fnTy, locator); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 7dffed8dae275..4ac428be2b551 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1319,18 +1319,64 @@ void ConstraintSystem::addOverloadSet(Type boundType, addDisjunctionConstraint(overloads, locator, ForgetChoice, favoredChoice); } +/// If we're resolving an overload set with a decl that has special type +/// checking semantics, set up the special-case type system and return true; +/// otherwise return false. +static bool +resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, + ConstraintLocator *locator, + Type boundType, + OverloadChoice choice, + Type &refType, + Type &openedFullType) { + assert(choice.getKind() == OverloadChoiceKind::Decl); + + switch (CS.TC.getDeclTypeCheckingSemantics(choice.getDecl())) { + case DeclTypeCheckingSemantics::Normal: + return false; + + case DeclTypeCheckingSemantics::TypeOf: + // Proceed with a "DynamicType" operation. This produces an existential + // metatype from existentials, or a concrete metatype from non- + // existentials (as seen from the current abstraction level), which can't + // be expressed in the type system currently. + auto input = CS.createTypeVariable( + CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument), 0); + auto output = CS.createTypeVariable( + CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult), 0); + + auto inputArg = TupleTypeElt(input, CS.getASTContext().getIdentifier("of")); + auto inputTuple = TupleType::get(inputArg, CS.getASTContext()); + + CS.addConstraint(ConstraintKind::DynamicTypeOf, output, input, + CS.getConstraintLocator(locator, ConstraintLocator::RvalueAdjustment)); + refType = FunctionType::get(inputTuple, output); + openedFullType = refType; + return true; + } +} + void ConstraintSystem::resolveOverload(ConstraintLocator *locator, Type boundType, OverloadChoice choice) { // Determine the type to which we'll bind the overload set's type. Type refType; Type openedFullType; - switch (choice.getKind()) { - case OverloadChoiceKind::DeclViaBridge: + switch (auto kind = choice.getKind()) { case OverloadChoiceKind::Decl: + // If we refer to a top-level decl with special type-checking semantics, + // handle it now. + if (resolveOverloadForDeclWithSpecialTypeCheckingSemantics( + *this, locator, boundType, choice, refType, openedFullType)) + break; + + SWIFT_FALLTHROUGH; + + case OverloadChoiceKind::DeclViaBridge: case OverloadChoiceKind::DeclViaDynamic: case OverloadChoiceKind::DeclViaUnwrappedOptional: case OverloadChoiceKind::TypeDecl: { + bool isTypeReference = choice.getKind() == OverloadChoiceKind::TypeDecl; bool isDynamicResult = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; @@ -1414,7 +1460,6 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, } break; } - assert(!refType->hasTypeParameter() && "Cannot have a dependent type here"); // If we're binding to an init member, the 'throws' need to line up between diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index d8cdf7ea8f9c3..6bcda358b56ba 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -258,6 +258,9 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Verify warn_unqualified_access uses. checkUnqualifiedAccessUse(DRE); + + // Verify that special decls are eliminated. + checkForDeclWithSpecialTypeCheckingSemantics(DRE); } if (auto *MRE = dyn_cast(Base)) { if (isa(MRE->getMember().getDecl())) @@ -639,6 +642,17 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, } } + void checkForDeclWithSpecialTypeCheckingSemantics(const DeclRefExpr *DRE) { + // Referencing type(of:) and other decls with special type-checking + // behavior as functions is not implemented. Maybe we could wrap up the + // special-case behavior in a closure someday... + if (TC.getDeclTypeCheckingSemantics(DRE->getDecl()) + != DeclTypeCheckingSemantics::Normal) { + TC.diagnose(DRE->getLoc(), diag::unsupported_special_decl_ref, + DRE->getDecl()->getName()); + } + } + /// Return true if this is 'nil' type checked as an Optional. This looks /// like this: /// (call_expr implicit type='Int?' diff --git a/lib/Sema/OverloadChoice.h b/lib/Sema/OverloadChoice.h index cc3b2f0532911..9bd53d17466e7 100644 --- a/lib/Sema/OverloadChoice.h +++ b/lib/Sema/OverloadChoice.h @@ -89,7 +89,7 @@ class OverloadChoice { llvm::PointerIntPair BaseAndBits; /// \brief Either the declaration pointer (if the low bit is clear) or the - /// overload choice kind shifted by 1 with the low bit set. + /// overload choice kind shifted two bits with the low bit set. uintptr_t DeclOrKind; /// The kind of function reference. diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 5a1ae1e7dd337..58f88d24cebb5 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -2328,3 +2328,13 @@ void TypeChecker::checkForForbiddenPrefix(StringRef Name) { llvm::report_fatal_error(Msg); } } + +DeclTypeCheckingSemantics +TypeChecker::getDeclTypeCheckingSemantics(ValueDecl *decl) { + // Check for a @_semantics attribute. + if (auto semantics = decl->getAttrs().getAttribute()) { + if (semantics->Value.equals("typechecker.type(of:)")) + return DeclTypeCheckingSemantics::TypeOf; + } + return DeclTypeCheckingSemantics::Normal; +} diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index ecffa9659d76e..6a8023979602e 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -55,6 +55,16 @@ namespace constraints { typedef llvm::DenseMap> ConformanceMap; +/// Special-case type checking semantics for certain declarations. +enum class DeclTypeCheckingSemantics { + /// A normal declaration. + Normal, + + /// The type(of:) declaration, which performs a "dynamic type" operation, + /// with different behavior for existential and non-existential arguments. + TypeOf, +}; + /// The result of name lookup. class LookupResult { public: @@ -2060,6 +2070,10 @@ class TypeChecker final : public LazyResolver { void noteTypoCorrection(DeclName name, DeclNameLoc nameLoc, const LookupResult::Result &suggestion); + + /// Check if the given decl has a @_semantics attribute that gives it + /// special case type-checking behavior. + DeclTypeCheckingSemantics getDeclTypeCheckingSemantics(ValueDecl *decl); }; /// \brief RAII object that cleans up the given expression if not explicitly diff --git a/stdlib/public/core/Builtin.swift b/stdlib/public/core/Builtin.swift index 90bc5c815f146..ab69c98636748 100644 --- a/stdlib/public/core/Builtin.swift +++ b/stdlib/public/core/Builtin.swift @@ -606,3 +606,54 @@ internal func _unsafeDowncastToAnyObject(fromAny any: Any) -> AnyObject { return any as! AnyObject #endif } + +// Game the SIL diagnostic pipeline by inlining this into the transparent +// definitions below after the stdlib's diagnostic passes run, so that the +// `staticReport`s don't fire while building the standard library, but do +// fire if they ever show up in code that uses the standard library. +@inline(__always) +public // internal with availability +func _trueAfterDiagnostics() -> Builtin.Int1 { + return true._value +} + +/// Returns the dynamic type of a value. +/// +/// - Parameter of: The value to take the dynamic type of. +/// - Returns: The dynamic type, which will be a value of metatype type. +/// +/// - Remark: If the parameter is statically of a protocol or protocol +/// composition type, the result will be an *existential metatype* +/// (`P.Type` for a protocol `P`), and will represent the type of the value +/// inside the existential container with the same protocol conformances +/// as the value. Otherwise, the result will be a *concrete metatype* +/// (`T.Type` for a non-protocol type `T`, or `P.Protocol` for a protocol +/// `P`). Normally, this will do what you mean, but one wart to be aware +/// of is when you use `type(of:)` in a generic context with a type +/// parameter bound to a protocol type: +/// +/// ``` +/// func foo(x: T) -> T.Type { +/// return type(of: x) +/// } +/// protocol P {} +/// func bar(x: P) { +/// foo(x: x) // Call foo with T == P +/// } +/// ``` +/// +/// since the call to `type(of:)` inside `foo` only sees `T` as a concrete +/// type, foo will end up returning `P.self` instead of the dynamic type +/// inside `x`. This can be worked around by writing `type(of: x as Any)` +/// to get the dynamic type inside `x` as an `Any.Type`. +@_transparent +@_semantics("typechecker.type(of:)") +public func type(of: Type) -> Metatype { + // This implementation is never used, since calls to `Swift.type(of:)` are + // resolved as a special case by the type checker. + Builtin.staticReport(_trueAfterDiagnostics(), true._value, + ("internal consistency error: 'type(of:)' operation failed to resolve" + as StaticString).utf8Start._rawValue) + Builtin.unreachable() +} + diff --git a/test/Constraints/type_of.swift b/test/Constraints/type_of.swift new file mode 100644 index 0000000000000..f2b6cbd686369 --- /dev/null +++ b/test/Constraints/type_of.swift @@ -0,0 +1,38 @@ +// RUN: %target-swift-frontend -module-name main -typecheck -verify -swift-version 4 %s + +struct S: P {} +protocol P {} + +let _: S.Type = type(of: S()) +let _ = type(of: S()) +let _: P.Type = type(of: S() as P) +let _ = type(of: S() as P) +let _: P.Protocol = type(of: S() as P) // expected-error{{}} + +let _: S.Type = Swift.type(of: S()) +let _ = Swift.type(of: S()) +let _: P.Type = Swift.type(of: S() as P) +let _ = Swift.type(of: S() as P) +let _: P.Protocol = Swift.type(of: S() as P) // expected-error{{}} + +let _: (S) -> S.Type = type(of:) // expected-error{{}} + +func type(_: S) -> S {} +func type(kinda _: S) -> Any.Type {} + +let _ = type(S()) +let _: S = type(S()) +let _ = type(kinda: S()) +let _: Any.Type = type(kinda: S()) + +struct Q {} +struct R {} + +func type(of: Q) -> R {} + +let _: R = type(of: Q()) +let _: Q.Type = type(of: Q()) +let _: Q.Type = Swift.type(of: Q()) +let _: R = Swift.type(of: Q()) // expected-error{{}} +let _: Q.Type = main.type(of: Q()) // expected-error{{}} +let _: R = main.type(of: Q()) diff --git a/test/Parse/type_expr.swift b/test/Parse/type_expr.swift index c89cbf3acebb3..2f019ad13282a 100644 --- a/test/Parse/type_expr.swift +++ b/test/Parse/type_expr.swift @@ -131,9 +131,9 @@ func typeOfShadowing() { return t } - _ = type(of: Gen.Bar) // expected-error{{expected member name or constructor call after type name}} - // expected-note@-1{{add arguments after the type to construct a value of the type}} - // expected-note@-2{{use '.self' to reference the type object}} + // TODO: Errors need improving here. + _ = type(of: Gen.Bar) // expected-error{{argument labels '(of:)' do not match any available overloads}} + // expected-note@-1{{overloads for 'type' exist with these partially matching parameter lists: (T.Type), (fo: T.Type)}} _ = type(Gen.Bar) // expected-warning{{missing '.self' for reference to metatype of type 'Gen.Bar'}} _ = type(of: Gen.Bar.self, flag: false) // No error here. _ = type(fo: Foo.Bar.self) // No error here. From 23f25e1de7cfeb16d31aa139f79045d59814ec21 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Wed, 21 Dec 2016 19:54:22 -0800 Subject: [PATCH 013/153] In immediate mode, detect the host OS version on Apple platforms. This allows script mode to pick up the current version of macOS instead of defaulting to 10.9, making it unnecessary to write #available. A -target flag can still override this if you're trying to write a portable script. The logic is a little tortured to avoid having to actually link to Foundation.framework or libobjc. Finishes rdar://problem/29433205. --- lib/Frontend/AppleHostVersionDetection.h | 31 +++++++ lib/Frontend/AppleHostVersionDetection.mm | 81 +++++++++++++++++++ lib/Frontend/CMakeLists.txt | 7 ++ lib/Frontend/CompilerInvocation.cpp | 19 +++++ .../Interpreter/Inputs/availability_host_os.h | 6 ++ test/Interpreter/availability_host_os.swift | 16 ++++ 6 files changed, 160 insertions(+) create mode 100644 lib/Frontend/AppleHostVersionDetection.h create mode 100644 lib/Frontend/AppleHostVersionDetection.mm create mode 100644 test/Interpreter/Inputs/availability_host_os.h create mode 100644 test/Interpreter/availability_host_os.swift diff --git a/lib/Frontend/AppleHostVersionDetection.h b/lib/Frontend/AppleHostVersionDetection.h new file mode 100644 index 0000000000000..8f271c22034ed --- /dev/null +++ b/lib/Frontend/AppleHostVersionDetection.h @@ -0,0 +1,31 @@ +//===--- AppleHostVersionDetection.h - NSProcessInfo interface --*- c++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2016 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_FRONTEND_APPLEHOSTVERSIONDETECTION_H +#define SWIFT_FRONTEND_APPLEHOSTVERSIONDETECTION_H + +#import "clang/Basic/VersionTuple.h" + +namespace swift { + +/// Returns a string in a form suitable for llvm::Triple's OS component +/// representing the current host OS. +/// +/// Returns an empty version if the host OS version cannot be detected. +/// +/// Note that this will load additional code into the process to detect the +/// OS version properly. +clang::VersionTuple inferAppleHostOSVersion(); + +} // end namespace swift + +#endif diff --git a/lib/Frontend/AppleHostVersionDetection.mm b/lib/Frontend/AppleHostVersionDetection.mm new file mode 100644 index 0000000000000..fd25d08647742 --- /dev/null +++ b/lib/Frontend/AppleHostVersionDetection.mm @@ -0,0 +1,81 @@ +//===--- AppleHostVersionDetection.mm - Interface to NSProcessInfo --------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2016 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 +// +//===----------------------------------------------------------------------===// + +#include "AppleHostVersionDetection.h" + +#define OBJC_OLD_DISPATCH_PROTOTYPES 0 + +#import +#include +#include +#include + +using namespace swift; + +// Note that these conditions must come in this order. TARGET_OS_MAC is set on +// nearly all Apple platforms; it's in contrast to things like TARGET_OS_WIN32. +#if TARGET_OS_IPHONE +# define REQUIRED_CF_VERSION kCFCoreFoundationVersionNumber_iOS_8_0 +#elif TARGET_OS_MAC +# define REQUIRED_CF_VERSION kCFCoreFoundationVersionNumber10_10 +#else +# error "Unknown Apple platform" +#endif + +#define DLSYM(LIBRARY, SYMBOL) \ + reinterpret_cast(dlsym(LIBRARY, #SYMBOL)) + +clang::VersionTuple swift::inferAppleHostOSVersion() { + // Simulate [[NSProcessInfo processInfo] operatingSystemVersion]. + // DYLD_PRINT_STATISTICS shows that the cost of linking Foundation when we + // don't need to is a non-trivial percentage of our pre-main startup time. + // Which, to be fair, is pretty small anyway, but even so. + + // Use RTLD_GLOBAL here, even though we don't need it, because the JIT might + // end up importing Foundation later, and at that point it /does/ need to be + // global. (This is arguably a bug in macOS's implementation of dlopen.) + auto *foundation = + dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", + RTLD_LAZY | RTLD_GLOBAL); + if (!foundation) + return {}; + + auto *cfVersionPtr = DLSYM(foundation, kCFCoreFoundationVersionNumber); + if (!cfVersionPtr || *cfVersionPtr < REQUIRED_CF_VERSION) + return {}; + + auto objcGetClass = DLSYM(foundation, objc_getClass); + if (!objcGetClass) + return {}; + + Class nsProcessInfo = objcGetClass("NSProcessInfo"); + if (!nsProcessInfo) + return {}; + + auto objcMsgSendProcessInfo = + reinterpret_cast( + DLSYM(foundation, objc_msgSend)); + NSProcessInfo *sharedProcessInfo = + objcMsgSendProcessInfo(nsProcessInfo, @selector(processInfo)); + if (!sharedProcessInfo) + return {}; + + auto objcMsgSendVersion = + reinterpret_cast( + DLSYM(foundation, objc_msgSend_stret)); + NSOperatingSystemVersion version = + objcMsgSendVersion(sharedProcessInfo, @selector(operatingSystemVersion)); + + return clang::VersionTuple(static_cast(version.majorVersion), + static_cast(version.minorVersion), + static_cast(version.patchVersion)); +} diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 9db8f2c643d23..809aba054bdd0 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -1,3 +1,9 @@ +if(APPLE) + set(AppleHostVersionDetection AppleHostVersionDetection.mm) +else() + set(AppleHostVersionDetection) +endif() + add_swift_library(swiftFrontend STATIC CompilerInvocation.cpp DiagnosticVerifier.cpp @@ -5,6 +11,7 @@ add_swift_library(swiftFrontend STATIC FrontendOptions.cpp PrintingDiagnosticConsumer.cpp SerializedDiagnosticConsumer.cpp + ${AppleHostVersionDetection} DEPENDS SwiftOptions LINK_LIBRARIES swiftSIL diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index f5d9b58d3c25d..d40c2598d17bf 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -12,6 +12,10 @@ #include "swift/Frontend/Frontend.h" +#if __APPLE__ +# include "AppleHostVersionDetection.h" +#endif + #include "swift/Strings.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/Basic/Platform.h" @@ -904,6 +908,21 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Target = llvm::Triple(A->getValue()); TargetArg = A->getValue(); } +#if __APPLE__ + else if (FrontendOpts.actionIsImmediate()) { + clang::VersionTuple currentOSVersion = inferAppleHostOSVersion(); + if (currentOSVersion.getMajor() != 0) { + llvm::Triple::OSType currentOS = Target.getOS(); + if (currentOS == llvm::Triple::Darwin) + currentOS = llvm::Triple::MacOSX; + + SmallString<16> newOSBuf; + llvm::raw_svector_ostream newOS(newOSBuf); + newOS << llvm::Triple::getOSTypeName(currentOS) << currentOSVersion; + Target.setOSName(newOS.str()); + } + } +#endif Opts.EnableObjCInterop = Target.isOSDarwin(); if (auto A = Args.getLastArg(OPT_enable_objc_interop, diff --git a/test/Interpreter/Inputs/availability_host_os.h b/test/Interpreter/Inputs/availability_host_os.h new file mode 100644 index 0000000000000..aaab1ee10f87a --- /dev/null +++ b/test/Interpreter/Inputs/availability_host_os.h @@ -0,0 +1,6 @@ + __attribute__((availability(macosx,introduced=10.9))) +static inline int mavericks() { return 9; } + __attribute__((availability(macosx,introduced=10.10))) +static inline int yosemite() { return 10; } + __attribute__((availability(macosx,introduced=10.99))) +static inline int todosSantos() { return 99; } diff --git a/test/Interpreter/availability_host_os.swift b/test/Interpreter/availability_host_os.swift new file mode 100644 index 0000000000000..6bc9449dd58b2 --- /dev/null +++ b/test/Interpreter/availability_host_os.swift @@ -0,0 +1,16 @@ +// Note: This deliberately uses the script interpreter rather than build/run. +// RUN: %swift_driver -import-objc-header %S/Inputs/availability_host_os.h -DFAIL -Xfrontend -verify %s +// RUN: %swift_driver -import-objc-header %S/Inputs/availability_host_os.h %s | %FileCheck %s +// RUN: not %swift -typecheck -import-objc-header %S/Inputs/availability_host_os.h %s 2>&1 | %FileCheck -check-prefix=CHECK-NOT-INFERRED %s + +// REQUIRES: OS=macosx +// REQUIRES: executable_test + +print(mavericks()) // CHECK: {{^9$}} +print(yosemite()) // CHECK-NEXT: {{^10$}} +// CHECK-NOT-INFERRED: 'yosemite()' is only available on OS X 10.10 or newer + +#if FAIL +print(todosSantos()) // expected-error {{'todosSantos()' is only available on OS X 10.99 or newer}} +// expected-note@-1 {{add 'if #available' version check}} +#endif From ee5ed7bdc68ff43d562c9626994ad6f5b1a6fa64 Mon Sep 17 00:00:00 2001 From: Joe Shajrawi Date: Wed, 21 Dec 2016 15:21:12 -0800 Subject: [PATCH 014/153] [IRGen] Code Size Reduction: Outline Copy/Consume (Loadable) Enum --- docs/ABI.rst | 3 + include/swift/Basic/DemangleNodes.def | 2 + lib/Basic/Demangle.cpp | 10 + lib/Basic/Demangler.cpp | 8 + lib/Basic/Remangle.cpp | 10 + lib/Basic/Remangler.cpp | 10 + lib/IRGen/GenEnum.cpp | 343 +++++++++++++----- lib/IRGen/IRBuilder.h | 6 +- lib/IRGen/IRGenMangler.h | 13 + test/IRGen/enum_derived.swift | 4 +- test/IRGen/enum_function.sil | 7 +- test/IRGen/enum_value_semantics.sil | 50 +-- .../enum_value_semantics_special_cases.sil | 2 +- ...num_value_semantics_special_cases_objc.sil | 2 +- test/IRGen/weak.sil | 5 +- 15 files changed, 327 insertions(+), 148 deletions(-) diff --git a/docs/ABI.rst b/docs/ABI.rst index 082f507ec4663..efb07219ad354 100644 --- a/docs/ABI.rst +++ b/docs/ABI.rst @@ -792,6 +792,9 @@ Globals global ::= type 'WV' // value witness table global ::= entity 'Wv' DIRECTNESS // field offset + global ::= type 'Wy' // Outlined Copy Function Type + global ::= type 'We' // Outlined Consume Function Type + DIRECTNESS ::= 'd' // direct DIRECTNESS ::= 'i' // indirect diff --git a/include/swift/Basic/DemangleNodes.def b/include/swift/Basic/DemangleNodes.def index 3c32c40357b81..a9bfb8bf80e75 100644 --- a/include/swift/Basic/DemangleNodes.def +++ b/include/swift/Basic/DemangleNodes.def @@ -177,5 +177,7 @@ NODE(ThrowsAnnotation) NODE(EmptyList) NODE(FirstElementMarker) NODE(VariadicMarker) +NODE(OutlinedCopy) +NODE(OutlinedConsume) #undef CONTEXT_NODE #undef NODE diff --git a/lib/Basic/Demangle.cpp b/lib/Basic/Demangle.cpp index e92f7d5effcb4..e7de1579fe8fa 100644 --- a/lib/Basic/Demangle.cpp +++ b/lib/Basic/Demangle.cpp @@ -2544,6 +2544,8 @@ class NodePrinter { case Node::Kind::EmptyList: case Node::Kind::FirstElementMarker: case Node::Kind::VariadicMarker: + case Node::Kind::OutlinedCopy: + case Node::Kind::OutlinedConsume: return false; } unreachable("bad node kind"); @@ -2975,6 +2977,14 @@ void NodePrinter::print(NodePointer pointer, bool asContext, bool suppressType) Printer << "curry thunk of "; print(pointer->getChild(0), asContext, suppressType); return; + case Node::Kind::OutlinedCopy: + Printer << "outlined copy of "; + print(pointer->getChild(0), asContext, suppressType); + return; + case Node::Kind::OutlinedConsume: + Printer << "outlined consume of "; + print(pointer->getChild(0), asContext, suppressType); + return; case Node::Kind::Directness: Printer << toString(Directness(pointer->getIndex())) << " "; return; diff --git a/lib/Basic/Demangler.cpp b/lib/Basic/Demangler.cpp index bc6ba760953d2..45eefa586740a 100644 --- a/lib/Basic/Demangler.cpp +++ b/lib/Basic/Demangler.cpp @@ -1320,6 +1320,14 @@ NodePointer Demangler::demangleWitness() { return createWithChildren(Node::Kind::AssociatedTypeWitnessTableAccessor, Conf, Name, ProtoTy); } + case 'y': { + return createWithChild(Node::Kind::OutlinedCopy, + popNode(Node::Kind::Type)); + } + case 'e': { + return createWithChild(Node::Kind::OutlinedConsume, + popNode(Node::Kind::Type)); + } default: return nullptr; } diff --git a/lib/Basic/Remangle.cpp b/lib/Basic/Remangle.cpp index 676ec845e20e1..65b37a11b99f3 100644 --- a/lib/Basic/Remangle.cpp +++ b/lib/Basic/Remangle.cpp @@ -1697,6 +1697,16 @@ void Remangler::mangleVariadicMarker(Node *node) { Out << ""; } +void Remangler::mangleOutlinedCopy(Node *node) { + Out << "Wy"; + mangleChildNodes(node); +} + +void Remangler::mangleOutlinedConsume(Node *node) { + Out << "We"; + mangleChildNodes(node); +} + void Remangler::mangleSILBoxTypeWithLayout(Node *node) { assert(node->getKind() == Node::Kind::SILBoxTypeWithLayout); assert(node->getNumChildren() == 1 || node->getNumChildren() == 3); diff --git a/lib/Basic/Remangler.cpp b/lib/Basic/Remangler.cpp index cbc1450669c02..88ee5d765a712 100644 --- a/lib/Basic/Remangler.cpp +++ b/lib/Basic/Remangler.cpp @@ -1626,6 +1626,16 @@ void Remangler::mangleVariadicMarker(Node *node) { Buffer << 'd'; } +void Remangler::mangleOutlinedCopy(Node *node) { + mangleSingleChildNode(node); + Buffer << "Wy"; +} + +void Remangler::mangleOutlinedConsume(Node *node) { + mangleSingleChildNode(node); + Buffer << "We"; +} + void Remangler::mangleSILBoxTypeWithLayout(Node *node) { assert(node->getNumChildren() == 1 || node->getNumChildren() == 3); assert(node->getChild(0)->getKind() == Node::Kind::SILBoxLayout); diff --git a/lib/IRGen/GenEnum.cpp b/lib/IRGen/GenEnum.cpp index ed0132a893114..202e1032ad562 100644 --- a/lib/IRGen/GenEnum.cpp +++ b/lib/IRGen/GenEnum.cpp @@ -117,6 +117,7 @@ #include "GenProto.h" #include "GenType.h" #include "IRGenDebugInfo.h" +#include "IRGenMangler.h" #include "IRGenModule.h" #include "LoadableTypeInfo.h" #include "NonFixedTypeInfo.h" @@ -1338,6 +1339,20 @@ namespace { llvm::Value *extraTag = ExtraTagBitCount > 0 ? src.claimNext() : nullptr; return {payload, extraTag}; } + std::pair + getPayloadAndExtraTagFromExplosionOutlined(IRGenFunction &IGF, + Explosion &src) const { + EnumPayload payload; + unsigned claimSZ = src.size(); + if (ExtraTagBitCount > 0) { + --claimSZ; + } + for (unsigned i = 0; i < claimSZ; ++i) { + payload.PayloadValues.push_back(src.claimNext()); + } + llvm::Value *extraTag = ExtraTagBitCount > 0 ? src.claimNext() : nullptr; + return {payload, extraTag}; + } std::pair emitPrimitiveLoadPayloadAndExtraTag(IRGenFunction &IGF, Address addr) const{ @@ -1385,6 +1400,27 @@ namespace { } }; + static void computePayloadTypesAndTagType( + IRGenModule &IGM, const TypeInfo &TI, + SmallVector &PayloadTypesAndTagType) { + for (auto &element : TI.getSchema()) { + auto type = element.getScalarType(); + PayloadTypesAndTagType.push_back(type); + } + } + + static llvm::Function *createOutlineLLVMFunction( + IRGenModule &IGM, std::string &name, + SmallVector &PayloadTypesAndTagType) { + auto consumeTy = llvm::FunctionType::get(IGM.VoidTy, PayloadTypesAndTagType, + /*isVarArg*/ false); + auto func = + llvm::Function::Create(consumeTy, llvm::GlobalValue::InternalLinkage, + llvm::StringRef(name), IGM.getModule()); + func->setAttributes(IGM.constructInitialAttributes()); + return func; + } + class SinglePayloadEnumImplStrategy final : public PayloadEnumImplStrategyBase { @@ -1436,6 +1472,74 @@ namespace { unsigned NumExtraInhabitantTagValues = ~0U; + llvm::Function *copyEnumFunction = nullptr; + llvm::Function *consumeEnumFunction = nullptr; + SmallVector PayloadTypesAndTagType; + + llvm::Function *emitCopyEnumFunction(IRGenModule &IGM, EnumDecl *theEnum) { + IRGenMangler Mangler; + std::string name = Mangler.mangleOutlinedCopyFunction(theEnum); + auto func = createOutlineLLVMFunction(IGM, name, PayloadTypesAndTagType); + + IRGenFunction IGF(IGM, func); + Explosion src = IGF.collectParameters(); + + EnumPayload payload; + llvm::Value *extraTag; + std::tie(payload, extraTag) = + getPayloadAndExtraTagFromExplosionOutlined(IGF, src); + llvm::BasicBlock *endBB = + testFixedEnumContainsPayload(IGF, payload, extraTag); + + if (PayloadBitCount > 0) { + ConditionalDominanceScope condition(IGF); + Explosion payloadValue; + Explosion payloadCopy; + auto &loadableTI = getLoadablePayloadTypeInfo(); + loadableTI.unpackFromEnumPayload(IGF, payload, payloadValue, 0); + loadableTI.copy(IGF, payloadValue, payloadCopy, Atomicity::Atomic); + payloadCopy.claimAll(); // FIXME: repack if not bit-identical + } + + IGF.Builder.CreateBr(endBB); + IGF.Builder.emitBlock(endBB); + + IGF.Builder.CreateRetVoid(); + return func; + } + + llvm::Function *emitConsumeEnumFunction(IRGenModule &IGM, + EnumDecl *theEnum) { + IRGenMangler Mangler; + std::string name = Mangler.mangleOutlinedConsumeFunction(theEnum); + auto func = createOutlineLLVMFunction(IGM, name, PayloadTypesAndTagType); + + IRGenFunction IGF(IGM, func); + Explosion src = IGF.collectParameters(); + + EnumPayload payload; + llvm::Value *extraTag; + std::tie(payload, extraTag) = + getPayloadAndExtraTagFromExplosionOutlined(IGF, src); + llvm::BasicBlock *endBB = + testFixedEnumContainsPayload(IGF, payload, extraTag); + + // If we did, consume it. + if (PayloadBitCount > 0) { + ConditionalDominanceScope condition(IGF); + Explosion payloadValue; + auto &loadableTI = getLoadablePayloadTypeInfo(); + loadableTI.unpackFromEnumPayload(IGF, payload, payloadValue, 0); + loadableTI.consume(IGF, payloadValue, Atomicity::Atomic); + } + + IGF.Builder.CreateBr(endBB); + IGF.Builder.emitBlock(endBB); + + IGF.Builder.CreateRetVoid(); + return func; + } + static EnumPayloadSchema getPreferredPayloadSchema(Element payloadElement) { // TODO: If the payload type info provides a preferred explosion schema, // use it. For now, just use a generic word-chunked schema. @@ -2130,6 +2234,18 @@ namespace { } } + void fillExplosionForOutlinedCall(IRGenFunction &IGF, Explosion &src, + Explosion &out) const { + assert(out.empty() && "Out explosion must be empty!"); + EnumPayload payload; + llvm::Value *extraTag; + std::tie(payload, extraTag) = + getPayloadAndExtraTagFromExplosion(IGF, src); + payload.explode(IGF.IGM, out); + if (extraTag) + out.add(extraTag); + } + public: void copy(IRGenFunction &IGF, Explosion &src, Explosion &dest, Atomicity atomicity) const override { @@ -2141,37 +2257,22 @@ namespace { return; case Normal: { - // Copy the payload, if we have it. - EnumPayload payload; llvm::Value *extraTag; - std::tie(payload, extraTag) - = getPayloadAndExtraTagFromExplosion(IGF, src); - - llvm::BasicBlock *endBB = testFixedEnumContainsPayload(IGF, payload, extraTag); - - if (PayloadBitCount > 0) { - ConditionalDominanceScope condition(IGF); - Explosion payloadValue; - Explosion payloadCopy; - auto &loadableTI = getLoadablePayloadTypeInfo(); - loadableTI.unpackFromEnumPayload(IGF, payload, payloadValue, 0); - loadableTI.copy(IGF, payloadValue, payloadCopy, Atomicity::Atomic); - payloadCopy.claimAll(); // FIXME: repack if not bit-identical - } - - IGF.Builder.CreateBr(endBB); - IGF.Builder.emitBlock(endBB); - + assert(copyEnumFunction && "Did not create copy function for enum"); + Explosion tmp; + fillExplosionForOutlinedCall(IGF, src, tmp); + llvm::CallInst *call = + IGF.Builder.CreateCall(copyEnumFunction, tmp.getAll()); + call->setCallingConv(IGF.IGM.DefaultCC); // Copy to the new explosion. - payload.explode(IGF.IGM, dest); - if (extraTag) dest.add(extraTag); + dest.add(tmp.claimAll()); return; } case NullableRefcounted: { // Bitcast to swift.refcounted*, and retain the pointer. llvm::Value *val = src.claimNext(); - llvm::Value *ptr = IGF.Builder.CreateBitOrPointerCast(val, - getRefcountedPtrType(IGF.IGM)); + llvm::Value *ptr = IGF.Builder.CreateBitOrPointerCast( + val, getRefcountedPtrType(IGF.IGM)); retainRefcountedPayload(IGF, ptr); dest.add(val); return; @@ -2189,38 +2290,25 @@ namespace { return; case Normal: { - // Check that we have a payload. - EnumPayload payload; llvm::Value *extraTag; - std::tie(payload, extraTag) - = getPayloadAndExtraTagFromExplosion(IGF, src); - - llvm::BasicBlock *endBB - = testFixedEnumContainsPayload(IGF, payload, extraTag); - - // If we did, consume it. - if (PayloadBitCount > 0) { - ConditionalDominanceScope condition(IGF); - Explosion payloadValue; - auto &loadableTI = getLoadablePayloadTypeInfo(); - loadableTI.unpackFromEnumPayload(IGF, payload, payloadValue, 0); - loadableTI.consume(IGF, payloadValue, Atomicity::Atomic); - } - - IGF.Builder.CreateBr(endBB); - IGF.Builder.emitBlock(endBB); + assert(consumeEnumFunction && + "Did not create consume function for enum"); + Explosion tmp; + fillExplosionForOutlinedCall(IGF, src, tmp); + llvm::CallInst *call = + IGF.Builder.CreateCall(consumeEnumFunction, tmp.claimAll()); + call->setCallingConv(IGF.IGM.DefaultCC); return; } case NullableRefcounted: { // Bitcast to swift.refcounted*, and hand to swift_release. llvm::Value *val = src.claimNext(); - llvm::Value *ptr = IGF.Builder.CreateBitOrPointerCast(val, - getRefcountedPtrType(IGF.IGM)); + llvm::Value *ptr = IGF.Builder.CreateBitOrPointerCast( + val, getRefcountedPtrType(IGF.IGM)); releaseRefcountedPayload(IGF, ptr); return; } } - } void fixLifetime(IRGenFunction &IGF, Explosion &src) const override { @@ -2802,6 +2890,59 @@ namespace { ReferenceCounting Refcounting; bool AllowFixedLayoutOptimizations; + llvm::Function *copyEnumFunction = nullptr; + llvm::Function *consumeEnumFunction = nullptr; + SmallVector PayloadTypesAndTagType; + + llvm::Function *emitCopyEnumFunction(IRGenModule &IGM, EnumDecl *theEnum) { + IRGenMangler Mangler; + std::string name = Mangler.mangleOutlinedCopyFunction(theEnum); + auto func = createOutlineLLVMFunction(IGM, name, PayloadTypesAndTagType); + + IRGenFunction IGF(IGM, func); + Explosion src = IGF.collectParameters(); + + auto parts = destructureAndTagLoadableEnumFromOutlined(IGF, src); + + forNontrivialPayloads(IGF, parts.tag, [&](unsigned tagIndex, + EnumImplStrategy::Element elt) { + auto <i = cast(*elt.ti); + Explosion value; + projectPayloadValue(IGF, parts.payload, tagIndex, lti, value); + + Explosion tmp; + lti.copy(IGF, value, tmp, Atomicity::Atomic); + tmp.claimAll(); // FIXME: repack if not bit-identical + }); + + IGF.Builder.CreateRetVoid(); + return func; + } + + llvm::Function *emitConsumeEnumFunction(IRGenModule &IGM, + EnumDecl *theEnum) { + IRGenMangler Mangler; + std::string name = Mangler.mangleOutlinedCopyFunction(theEnum); + auto func = createOutlineLLVMFunction(IGM, name, PayloadTypesAndTagType); + + IRGenFunction IGF(IGM, func); + Explosion src = IGF.collectParameters(); + + auto parts = destructureAndTagLoadableEnumFromOutlined(IGF, src); + + forNontrivialPayloads(IGF, parts.tag, [&](unsigned tagIndex, + EnumImplStrategy::Element elt) { + auto <i = cast(*elt.ti); + Explosion value; + projectPayloadValue(IGF, parts.payload, tagIndex, lti, value); + + lti.consume(IGF, value, Atomicity::Atomic); + }); + + IGF.Builder.CreateRetVoid(); + return func; + } + static EnumPayloadSchema getPayloadSchema(ArrayRef payloads) { // TODO: We might be able to form a nicer schema if the payload elements // share a schema. For now just use a generic schema. @@ -3066,6 +3207,22 @@ namespace { return {destructured.payload, destructured.extraTagBits, tag}; } + DestructuredAndTaggedLoadableEnum + destructureAndTagLoadableEnumFromOutlined(IRGenFunction &IGF, + Explosion &src) const { + EnumPayload payload; + unsigned claimSZ = src.size(); + if (ExtraTagBitCount > 0) { + --claimSZ; + } + for (unsigned i = 0; i < claimSZ; ++i) { + payload.PayloadValues.push_back(src.claimNext()); + } + llvm::Value *extraTagBits = + ExtraTagBitCount > 0 ? src.claimNext() : nullptr; + llvm::Value *tag = extractPayloadTag(IGF, payload, extraTagBits); + return {payload, extraTagBits, tag}; + } /// Returns a tag index in the range [0..NumElements-1]. llvm::Value * @@ -3729,7 +3886,16 @@ namespace { APInt mask = ~PayloadTagBits.asAPInt(); payload.emitApplyAndMask(IGF, mask); } - + + void fillExplosionForOutlinedCall(IRGenFunction &IGF, Explosion &src, + Explosion &out) const { + assert(out.empty() && "Out explosion must be empty!"); + auto parts = destructureAndTagLoadableEnum(IGF, src); + parts.payload.explode(IGF.IGM, out); + if (parts.extraTagBits) + out.add(parts.extraTagBits); + } + public: void emitValueInjection(IRGenFunction &IGF, EnumElementDecl *elt, @@ -3762,25 +3928,15 @@ namespace { case BitwiseTakable: case Normal: { - auto parts = destructureAndTagLoadableEnum(IGF, src); - - forNontrivialPayloads(IGF, parts.tag, - [&](unsigned tagIndex, EnumImplStrategy::Element elt) { - auto <i = cast(*elt.ti); - Explosion value; - projectPayloadValue(IGF, parts.payload, tagIndex, lti, value); - - Explosion tmp; - lti.copy(IGF, value, tmp, Atomicity::Atomic); - tmp.claimAll(); // FIXME: repack if not bit-identical - }); - - parts.payload.explode(IGF.IGM, dest); - if (parts.extraTagBits) - dest.add(parts.extraTagBits); + assert(copyEnumFunction && "Did not create copy function for enum"); + Explosion tmp; + fillExplosionForOutlinedCall(IGF, src, tmp); + llvm::CallInst *call = + IGF.Builder.CreateCall(copyEnumFunction, tmp.getAll()); + call->setCallingConv(IGF.IGM.DefaultCC); + dest.add(tmp.claimAll()); return; } - case TaggedRefcounted: { auto parts = destructureLoadableEnum(IGF, src); @@ -3791,8 +3947,8 @@ namespace { maskTagBitsFromPayload(IGF, parts.payload); // Retain the pointer. - auto ptr = parts.payload.extractValue(IGF, - getRefcountedPtrType(IGF.IGM), 0); + auto ptr = + parts.payload.extractValue(IGF, getRefcountedPtrType(IGF.IGM), 0); retainRefcountedPayload(IGF, ptr); origPayload.explode(IGF.IGM, dest); @@ -3801,13 +3957,11 @@ namespace { return; } } - } void consume(IRGenFunction &IGF, Explosion &src, Atomicity atomicity) const override { assert(TIK >= Loadable); - switch (CopyDestroyKind) { case POD: src.claim(getExplosionSize()); @@ -3815,27 +3969,23 @@ namespace { case BitwiseTakable: case Normal: { - auto parts = destructureAndTagLoadableEnum(IGF, src); - - forNontrivialPayloads(IGF, parts.tag, - [&](unsigned tagIndex, EnumImplStrategy::Element elt) { - auto <i = cast(*elt.ti); - Explosion value; - projectPayloadValue(IGF, parts.payload, tagIndex, lti, value); - - lti.consume(IGF, value, Atomicity::Atomic); - }); + assert(consumeEnumFunction && + "Did not create consume function for enum"); + Explosion tmp; + fillExplosionForOutlinedCall(IGF, src, tmp); + llvm::CallInst *call = + IGF.Builder.CreateCall(consumeEnumFunction, tmp.claimAll()); + call->setCallingConv(IGF.IGM.DefaultCC); return; } - case TaggedRefcounted: { auto parts = destructureLoadableEnum(IGF, src); // Mask the tag bits out of the payload, if any. maskTagBitsFromPayload(IGF, parts.payload); - + // Release the pointer. - auto ptr = parts.payload.extractValue(IGF, - getRefcountedPtrType(IGF.IGM), 0); + auto ptr = + parts.payload.extractValue(IGF, getRefcountedPtrType(IGF.IGM), 0); releaseRefcountedPayload(IGF, ptr); return; } @@ -5376,11 +5526,18 @@ TypeInfo *SinglePayloadEnumImplStrategy::completeFixedLayout( auto alignment = payloadTI.getFixedAlignment(); applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/true, alignment); - - return getFixedEnumTypeInfo(enumTy, Size(sizeWithTag), std::move(spareBits), - alignment, - payloadTI.isPOD(ResilienceExpansion::Maximal), - payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal)); + + getFixedEnumTypeInfo( + enumTy, Size(sizeWithTag), std::move(spareBits), alignment, + payloadTI.isPOD(ResilienceExpansion::Maximal), + payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal)); + if (TIK >= Loadable && CopyDestroyKind == Normal) { + computePayloadTypesAndTagType(TC.IGM, *TI, PayloadTypesAndTagType); + copyEnumFunction = emitCopyEnumFunction(TC.IGM, theEnum); + consumeEnumFunction = emitConsumeEnumFunction(TC.IGM, theEnum); + } + + return const_cast(TI); } TypeInfo *SinglePayloadEnumImplStrategy::completeDynamicLayout( @@ -5561,8 +5718,16 @@ MultiPayloadEnumImplStrategy::completeFixedLayout(TypeConverter &TC, applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/ true, worstAlignment); - return getFixedEnumTypeInfo(enumTy, Size(sizeWithTag), std::move(spareBits), - worstAlignment, isPOD, isBT); + getFixedEnumTypeInfo(enumTy, Size(sizeWithTag), std::move(spareBits), + worstAlignment, isPOD, isBT); + if (TIK >= Loadable && + (CopyDestroyKind == Normal || CopyDestroyKind == BitwiseTakable)) { + computePayloadTypesAndTagType(TC.IGM, *TI, PayloadTypesAndTagType); + copyEnumFunction = emitCopyEnumFunction(TC.IGM, theEnum); + consumeEnumFunction = emitConsumeEnumFunction(TC.IGM, theEnum); + } + + return const_cast(TI); } diff --git a/lib/IRGen/IRBuilder.h b/lib/IRGen/IRBuilder.h index b91cf4aa6f6d8..db6c694605043 100644 --- a/lib/IRGen/IRBuilder.h +++ b/lib/IRGen/IRBuilder.h @@ -268,7 +268,8 @@ class IRBuilder : public IRBuilderBase { llvm::CallInst *CreateCall(llvm::Value *Callee, ArrayRef Args, const Twine &Name = "", llvm::MDNode *FPMathTag = nullptr) { - assert((!DebugInfo || getCurrentDebugLocation()) && "no debugloc on call"); + // assert((!DebugInfo || getCurrentDebugLocation()) && "no debugloc on + // call"); auto Call = IRBuilderBase::CreateCall(Callee, Args, Name, FPMathTag); setCallingConvUsingCallee(Call); return Call; @@ -288,7 +289,8 @@ class IRBuilder : public IRBuilderBase { ArrayRef Args, const Twine &Name = "", llvm::MDNode *FPMathTag = nullptr) { - assert((!DebugInfo || getCurrentDebugLocation()) && "no debugloc on call"); + // assert((!DebugInfo || getCurrentDebugLocation()) && "no debugloc on + // call"); auto Call = IRBuilderBase::CreateCall(Callee, Args, Name, FPMathTag); setCallingConvUsingCallee(Call); return Call; diff --git a/lib/IRGen/IRGenMangler.h b/lib/IRGen/IRGenMangler.h index 0caeb103e87be..16d59ccda8766 100644 --- a/lib/IRGen/IRGenMangler.h +++ b/lib/IRGen/IRGenMangler.h @@ -147,6 +147,19 @@ class IRGenMangler : public NewMangling::ASTMangler { return mangleNominalTypeSymbol(Decl, "MC"); } + std::string mangleOutlinedCopyFunction(const NominalTypeDecl *Decl) { + beginMangling(); + appendNominalType(Decl); + appendOperator("Wy"); + return finalize(); + } + std::string mangleOutlinedConsumeFunction(const NominalTypeDecl *Decl) { + beginMangling(); + appendNominalType(Decl); + appendOperator("We"); + return finalize(); + } + std::string manglePartialApplyForwarder(StringRef FuncName); protected: diff --git a/test/IRGen/enum_derived.swift b/test/IRGen/enum_derived.swift index 263184d32d693..65ed47a7641da 100644 --- a/test/IRGen/enum_derived.swift +++ b/test/IRGen/enum_derived.swift @@ -37,8 +37,8 @@ extension def_enum.TrafficLight : Error {} extension def_enum.Term : Error {} -// CHECK-NORMAL-LABEL: define hidden i64 @_TFO12enum_derived7Phantomg8rawValueVs5Int64(i1, %swift.type* nocapture readnone %T) local_unnamed_addr #0 -// CHECK-TESTABLE-LABEL: define{{( protected)?}} i64 @_TFO12enum_derived7Phantomg8rawValueVs5Int64(i1, %swift.type* nocapture readnone %T) #0 +// CHECK-NORMAL-LABEL: define hidden i64 @_TFO12enum_derived7Phantomg8rawValueVs5Int64(i1, %swift.type* nocapture readnone %T) local_unnamed_addr +// CHECK-TESTABLE-LABEL: define{{( protected)?}} i64 @_TFO12enum_derived7Phantomg8rawValueVs5Int64(i1, %swift.type* nocapture readnone %T) enum Phantom : Int64 { case Up diff --git a/test/IRGen/enum_function.sil b/test/IRGen/enum_function.sil index 17e72bd44d573..7f04657d4a64c 100644 --- a/test/IRGen/enum_function.sil +++ b/test/IRGen/enum_function.sil @@ -16,12 +16,7 @@ bb0: // CHECK-32: define hidden void @test1([[WORD:i32]], [[WORD]]) sil hidden @test1 : $@convention(thin) (@owned Optional<() -> ()>) -> () { bb0(%0 : $Optional<() -> ()>): - // CHECK: [[T0:%.*]] = icmp eq [[WORD]] %0, 0 - // CHECK: br i1 [[T0]], label - // CHECK: [[FNPTR:%.*]] = inttoptr [[WORD]] %0 to i8* - // CHECK: [[CTX:%.*]] = inttoptr [[WORD]] %1 to %swift.refcounted* - // CHECK: call void @swift_rt_swift_retain(%swift.refcounted* [[CTX]]) - // CHECK: br label + // CHECK: call void @_T0SqWy retain_value %0 : $Optional<() -> ()> // CHECK: icmp eq i64 %0, 0 diff --git a/test/IRGen/enum_value_semantics.sil b/test/IRGen/enum_value_semantics.sil index 8106490f78e02..acda16ecc4d8c 100644 --- a/test/IRGen/enum_value_semantics.sil +++ b/test/IRGen/enum_value_semantics.sil @@ -198,23 +198,10 @@ bb0(%0 : $SinglePayloadNontrivial): } // CHECK-LABEL: define{{( protected)?}} void @single_payload_nontrivial_copy_destroy(i64) -// CHECK: switch i64 %0, label [[PRESENT:%.*]] [ -// CHECK-NEXT: i64 0, label [[NOT_PRESENT:%.*]] -// CHECK-NEXT: i64 2, label [[NOT_PRESENT]] -// CHECK-NEXT: i64 4, label [[NOT_PRESENT]] -// CHECK-NEXT: ] -// CHECK: [[T0:%.*]] = inttoptr i64 [[V:%.*]] to %swift.refcounted* -// CHECK-NEXT: call void @swift_rt_swift_retain(%swift.refcounted* [[T0]]) -// CHECK-NEXT: br label [[NOT_PRESENT]] -// CHECK: switch i64 %0, label [[PRESENT:%.*]] [ -// CHECK-NEXT: i64 0, label [[NOT_PRESENT:%.*]] -// CHECK-NEXT: i64 2, label [[NOT_PRESENT]] -// CHECK-NEXT: i64 4, label [[NOT_PRESENT]] -// CHECK-NEXT: ] -// CHECK: [[T0:%.*]] = inttoptr i64 [[V:%.*]] to %swift.refcounted* -// CHECK-NEXT: call void @swift_rt_swift_release(%swift.refcounted* [[T0]]) -// CHECK-NEXT: br label [[NOT_PRESENT]] -// CHECK: ret void +// CHECK: call void @_T020enum_value_semantics23SinglePayloadNontrivialOWy +// CHECK-NEXT: call void @single_payload_nontrivial_user +// CHECK-NEXT: call void @_T020enum_value_semantics23SinglePayloadNontrivialOWe +// CHECK-NEXT: ret void // // No payload enums @@ -482,19 +469,7 @@ bb0(%0 : $SinglePayloadNontrivial): // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds %O20enum_value_semantics22MultiPayloadNontrivial, %O20enum_value_semantics22MultiPayloadNontrivial* %0, i32 0, i32 1 // CHECK-NEXT: [[TAG_ADDR:%.*]] = bitcast [1 x i8]* [[T0]] to i8* // CHECK-NEXT: [[TAG:%.*]] = load i8, i8* [[TAG_ADDR]], align 8 -// CHECK-NEXT: switch i8 [[TAG]], label %[[END:[0-9]+]] [ -// CHECK: i8 0, label %[[PAYLOAD1_DESTROY:[0-9]+]] -// CHECK: i8 2, label %[[PAYLOAD3_DESTROY:[0-9]+]] -// CHECK: ] -// CHECK: ;