From 29d8bfd983e7bc3d0d59c5c2f7a7ee285f409f4b Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 13 May 2020 16:52:31 -0400 Subject: [PATCH 01/11] Make the lazy assignment of an assoociated type/wtable atomic. Should fix SR-12760. --- stdlib/public/runtime/Metadata.cpp | 45 ++++++++++++----- .../associated_witness_concurrency.swift | 48 +++++++++++++++++++ 2 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 test/Runtime/associated_witness_concurrency.swift diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 2c224d3765875..9d6c7013bb5a0 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -4654,6 +4654,8 @@ static StringRef findAssociatedTypeName(const ProtocolDescriptor *protocol, return StringRef(); } +using AssociatedTypeWitness = std::atomic; + SWIFT_CC(swift) static MetadataResponse swift_getAssociatedTypeWitnessSlowImpl( @@ -4677,8 +4679,8 @@ swift_getAssociatedTypeWitnessSlowImpl( // Retrieve the witness. unsigned witnessIndex = assocType - reqBase; - auto *witnessAddr = &((const Metadata **)wtable)[witnessIndex]; - auto witness = *witnessAddr; + auto *witnessAddr = &((AssociatedTypeWitness*)wtable)[witnessIndex]; + auto witness = witnessAddr->load(std::memory_order_acquire); #if SWIFT_PTRAUTH uint16_t extraDiscriminator = assocType->Flags.getExtraDiscriminator(); @@ -4781,8 +4783,14 @@ swift_getAssociatedTypeWitnessSlowImpl( if (response.State == MetadataState::Complete) { // We pass type metadata around as unsigned pointers, but we sign them // in witness tables, which doesn't provide all that much extra security. - initAssociatedTypeProtocolWitness(witnessAddr, assocTypeMetadata, - *assocType); + auto valueToStore = assocTypeMetadata; +#if SWIFT_PTRAUTH + valueToStore = ptrauth_sign_unauthenticated(valueToStore, + swift_ptrauth_key_associated_type, + ptrauth_blend_discriminator(witnessAddr, + extraDiscriminator)); +#endif + witnessAddr->store(valueToStore, std::memory_order_release); } return response; @@ -4799,8 +4807,8 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request, // If the low bit of the witness is clear, it's already a metadata pointer. unsigned witnessIndex = assocType - reqBase; - auto *witnessAddr = &((const void* *)wtable)[witnessIndex]; - auto witness = *witnessAddr; + auto *witnessAddr = &((const AssociatedTypeWitness *)wtable)[witnessIndex]; + auto witness = witnessAddr->load(std::memory_order_acquire); #if SWIFT_PTRAUTH uint16_t extraDiscriminator = assocType->Flags.getExtraDiscriminator(); @@ -4819,6 +4827,8 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request, reqBase, assocType); } +using AssociatedConformanceWitness = std::atomic; + SWIFT_CC(swift) static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl( WitnessTable *wtable, @@ -4844,8 +4854,8 @@ static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl( // Retrieve the witness. unsigned witnessIndex = assocConformance - reqBase; - auto *witnessAddr = &((void**)wtable)[witnessIndex]; - auto witness = *witnessAddr; + auto *witnessAddr = &((AssociatedConformanceWitness*)wtable)[witnessIndex]; + auto witness = witnessAddr->load(std::memory_order_acquire); #if SWIFT_PTRAUTH // For associated protocols, the witness is signed with address @@ -4903,9 +4913,18 @@ static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl( // The access function returns an unsigned pointer for now. - // We can't just use initAssociatedConformanceProtocolWitness because we - // also use this function for base protocols. - initProtocolWitness(witnessAddr, assocWitnessTable, *assocConformance); + auto valueToStore = assocWitnessTable; +#if SWIFT_PTRAUTH + if (assocConformance->Flags.isSignedWithAddress()) { + uint16_t extraDiscriminator = + assocConformance->Flags.getExtraDiscriminator(); + valueToStore = ptrauth_sign_unauthenticated(valueToStore, + swift_ptrauth_key_associated_conformance, + ptrauth_blend_discriminator(witnessAddr, + extraDiscriminator)); + } +#endif + witnessAddr->store(valueToStore, std::memory_order_release); return assocWitnessTable; } @@ -4926,8 +4945,8 @@ const WitnessTable *swift::swift_getAssociatedConformanceWitness( // Retrieve the witness. unsigned witnessIndex = assocConformance - reqBase; - auto *witnessAddr = &((const void* *)wtable)[witnessIndex]; - auto witness = *witnessAddr; + auto *witnessAddr = &((AssociatedConformanceWitness*)wtable)[witnessIndex]; + auto witness = witnessAddr->load(std::memory_order_acquire); #if SWIFT_PTRAUTH uint16_t extraDiscriminator = assocConformance->Flags.getExtraDiscriminator(); diff --git a/test/Runtime/associated_witness_concurrency.swift b/test/Runtime/associated_witness_concurrency.swift new file mode 100644 index 0000000000000..8dda259621249 --- /dev/null +++ b/test/Runtime/associated_witness_concurrency.swift @@ -0,0 +1,48 @@ +// RUN: %target-swiftc_driver %s -g %import-libdispatch -o %t +// RUN: %target-codesign %t +// RUN: %target-run %t +// REQUIRES: libdispatch +// REQUIRES: executable_test + +import Dispatch + +protocol P { + associatedtype Unused +} +struct A : P { + // We never actually read this associated type, but its presence in the + // wtable should force it to be instantiated rather than shared for + // all specializations of A. + typealias Unused = A> +} + +protocol Q { + associatedtype Assoc: P + + func foo() +} +struct B : Q { + // Both the metadata and the wtable for this associated type require + // runtime instantiation and must be fetched lazily. + typealias Assoc = A + + func foo() {} +} + +// It's possible that the optimizer might someday be able to recognize +// that this is a no-op. +func rundown(value: T, count: Int) { + guard count > 0 else { return } + + value.foo() + + // Assuming that T is B for some U, this constructs B>, + // which will be T in the recursive call; i.e. we should have a + // different metadata/wtable pair each time. In order to construct + // that, we have to read the associated metadata and wtable for T.Assoc. + rundown(value: B(), count: count - 1) +} + +DispatchQueue.concurrentPerform(iterations: 5) { n in + rundown(value: B>(), count: 1000) +} From 74c2106544a06b160462f631052f88cecbef15ac Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 13 May 2020 16:08:25 -0700 Subject: [PATCH 02/11] ReplaceOpaqueTypesWithUnderlyingTypes: Handle a type being "substituted" with itself. SIL type lowering might have already substituted away an opaque type during a SIL substitution. Fixes rdar://problem/62072397. --- lib/AST/Type.cpp | 17 +++++++++++++---- .../opaque_type_lowering_in_substitution.swift | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 test/SILGen/opaque_type_lowering_in_substitution.swift diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 3e29c740aa6c1..0f48d8def7543 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3043,12 +3043,21 @@ ProtocolConformanceRef ReplaceOpaqueTypesWithUnderlyingTypes:: operator()(CanType maybeOpaqueType, Type replacementType, ProtocolDecl *protocol) const { auto abstractRef = ProtocolConformanceRef(protocol); - + auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype(maybeOpaqueType); if (!archetypeAndRoot) { - assert(maybeOpaqueType->isTypeParameter() || - maybeOpaqueType->is()); - return abstractRef; + if (maybeOpaqueType->isTypeParameter() || + maybeOpaqueType->is()) + return abstractRef; + + // SIL type lowering may have already substituted away the opaque type, in + // which case we'll end up "substituting" the same type. + if (maybeOpaqueType->isEqual(replacementType)) { + return inContext->getParentModule() + ->lookupConformance(replacementType, protocol); + } + + llvm_unreachable("origType should have been an opaque type or type parameter"); } auto archetype = archetypeAndRoot->first; diff --git a/test/SILGen/opaque_type_lowering_in_substitution.swift b/test/SILGen/opaque_type_lowering_in_substitution.swift new file mode 100644 index 0000000000000..903de21148a03 --- /dev/null +++ b/test/SILGen/opaque_type_lowering_in_substitution.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-emit-silgen -disable-availability-checking -verify %s +protocol P {} +extension Int: P {} + +func foo() -> some P { return 0 } +func bar(_ x: T) -> some P { return x } + +struct Bas { init(_: T) {} } + +func abstraction_level(x: T) -> (T) -> () { + return { _ in () } +} + +func test() { + abstraction_level(x: Bas(bar(foo())))(Bas(bar(foo()))) +} From 1a4f523d1ca4d5615585d58f6ee0fc755f05ca0d Mon Sep 17 00:00:00 2001 From: tbkka Date: Thu, 14 May 2020 12:27:11 -0700 Subject: [PATCH 03/11] Reduce generics in Codable (#31278) (#31772) In particular, types generic on the CodingKey produce a lot of runtime metadata. Reducing the number of such types should help with some of the reported memory bloat from Codable. Based on a suggestion of @jckarter Resolves rdar://62620208 --- stdlib/public/core/Codable.swift | 618 +++++++++++++++++++------------ 1 file changed, 378 insertions(+), 240 deletions(-) diff --git a/stdlib/public/core/Codable.swift b/stdlib/public/core/Codable.swift index fbadc5c3f0181..4b7fa1abea676 100644 --- a/stdlib/public/core/Codable.swift +++ b/stdlib/public/core/Codable.swift @@ -498,9 +498,8 @@ public struct KeyedEncodingContainer : { public typealias Key = K - /// The container for the concrete encoder. The type is _*Base so that it's - /// generic on the key type. - internal var _box: _KeyedEncodingContainerBase + /// The container for the concrete encoder. + internal var _box: _KeyedEncodingContainerBase /// Creates a new instance with the given container. /// @@ -1467,9 +1466,8 @@ public struct KeyedDecodingContainer : { public typealias Key = K - /// The container for the concrete decoder. The type is _*Base so that it's - /// generic on the key type. - internal var _box: _KeyedDecodingContainerBase + /// The container for the concrete decoder. + internal var _box: _KeyedDecodingContainerBase /// Creates a new instance with the given container. /// @@ -1492,7 +1490,7 @@ public struct KeyedDecodingContainer : /// which are not convertible to one another. This should report all keys /// present which are convertible to the requested type. public var allKeys: [Key] { - return _box.allKeys + return _box.allKeys as! [Key] } /// Returns a Boolean value indicating whether the decoder contains a value @@ -3454,7 +3452,7 @@ extension DecodingError { // Keyed Encoding Container Implementations //===----------------------------------------------------------------------===// -internal class _KeyedEncodingContainerBase { +internal class _KeyedEncodingContainerBase { internal init(){} deinit {} @@ -3464,149 +3462,149 @@ internal class _KeyedEncodingContainerBase { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeNil(forKey key: Key) throws { + internal func encodeNil(forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: Bool, forKey key: Key) throws { + internal func encode(_ value: Bool, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: String, forKey key: Key) throws { + internal func encode(_ value: String, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: Double, forKey key: Key) throws { + internal func encode(_ value: Double, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: Float, forKey key: Key) throws { + internal func encode(_ value: Float, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: Int, forKey key: Key) throws { + internal func encode(_ value: Int, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: Int8, forKey key: Key) throws { + internal func encode(_ value: Int8, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: Int16, forKey key: Key) throws { + internal func encode(_ value: Int16, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: Int32, forKey key: Key) throws { + internal func encode(_ value: Int32, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: Int64, forKey key: Key) throws { + internal func encode(_ value: Int64, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: UInt, forKey key: Key) throws { + internal func encode(_ value: UInt, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: UInt8, forKey key: Key) throws { + internal func encode(_ value: UInt8, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: UInt16, forKey key: Key) throws { + internal func encode(_ value: UInt16, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: UInt32, forKey key: Key) throws { + internal func encode(_ value: UInt32, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: UInt64, forKey key: Key) throws { + internal func encode(_ value: UInt64, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encode(_ value: T, forKey key: Key) throws { + internal func encode(_ value: T, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeConditional( + internal func encodeConditional( _ object: T, - forKey key: Key + forKey key: K ) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: Bool?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: Bool?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: String?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: String?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: Double?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: Double?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: Float?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: Float?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: Int?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: Int?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: Int8?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: Int8?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: Int16?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: Int16?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: Int32?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: Int32?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: Int64?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: Int64?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: UInt?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: UInt?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: UInt8?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: UInt16?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: UInt32?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws { + internal func encodeIfPresent(_ value: UInt64?, forKey key: K) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func encodeIfPresent( + internal func encodeIfPresent( _ value: T?, - forKey key: Key + forKey key: K ) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func nestedContainer( + internal func nestedContainer( keyedBy keyType: NestedKey.Type, - forKey key: Key + forKey key: K ) -> KeyedEncodingContainer { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func nestedUnkeyedContainer( - forKey key: Key + internal func nestedUnkeyedContainer( + forKey key: K ) -> UnkeyedEncodingContainer { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } @@ -3615,14 +3613,14 @@ internal class _KeyedEncodingContainerBase { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } - internal func superEncoder(forKey key: Key) -> Encoder { + internal func superEncoder(forKey key: K) -> Encoder { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } } internal final class _KeyedEncodingContainerBox< Concrete: KeyedEncodingContainerProtocol ->: _KeyedEncodingContainerBase { +>: _KeyedEncodingContainerBase { typealias Key = Concrete.Key internal var concrete: Concrete @@ -3635,195 +3633,263 @@ internal final class _KeyedEncodingContainerBox< return concrete.codingPath } - override internal func encodeNil(forKey key: Key) throws { + override internal func encodeNil(forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeNil(forKey: key) } - override internal func encode(_ value: Bool, forKey key: Key) throws { + override internal func encode(_ value: Bool, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: String, forKey key: Key) throws { + override internal func encode(_ value: String, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: Double, forKey key: Key) throws { + override internal func encode(_ value: Double, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: Float, forKey key: Key) throws { + override internal func encode(_ value: Float, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: Int, forKey key: Key) throws { + override internal func encode(_ value: Int, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: Int8, forKey key: Key) throws { + override internal func encode(_ value: Int8, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: Int16, forKey key: Key) throws { + override internal func encode(_ value: Int16, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: Int32, forKey key: Key) throws { + override internal func encode(_ value: Int32, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: Int64, forKey key: Key) throws { + override internal func encode(_ value: Int64, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: UInt, forKey key: Key) throws { + override internal func encode(_ value: UInt, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: UInt8, forKey key: Key) throws { + override internal func encode(_ value: UInt8, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: UInt16, forKey key: Key) throws { + override internal func encode(_ value: UInt16, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: UInt32, forKey key: Key) throws { + override internal func encode(_ value: UInt32, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode(_ value: UInt64, forKey key: Key) throws { + override internal func encode(_ value: UInt64, forKey key: K) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encode( + override internal func encode( _ value: T, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encode(value, forKey: key) } - override internal func encodeConditional( + override internal func encodeConditional( _ object: T, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeConditional(object, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: Bool?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: String?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: Double?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: Float?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: Int?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: Int8?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: Int16?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: Int32?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: Int64?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: UInt?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: UInt8?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: UInt16?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: UInt32?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: UInt64?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func encodeIfPresent( + override internal func encodeIfPresent( _ value: T?, - forKey key: Key + forKey key: K ) throws { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) try concrete.encodeIfPresent(value, forKey: key) } - override internal func nestedContainer( + override internal func nestedContainer( keyedBy keyType: NestedKey.Type, - forKey key: Key + forKey key: K ) -> KeyedEncodingContainer { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return concrete.nestedContainer(keyedBy: NestedKey.self, forKey: key) } - override internal func nestedUnkeyedContainer( - forKey key: Key + override internal func nestedUnkeyedContainer( + forKey key: K ) -> UnkeyedEncodingContainer { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return concrete.nestedUnkeyedContainer(forKey: key) } @@ -3831,12 +3897,14 @@ internal final class _KeyedEncodingContainerBox< return concrete.superEncoder() } - override internal func superEncoder(forKey key: Key) -> Encoder { + override internal func superEncoder(forKey key: K) -> Encoder { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return concrete.superEncoder(forKey: key) } } -internal class _KeyedDecodingContainerBase { +internal class _KeyedDecodingContainerBase { internal init(){} deinit {} @@ -3845,237 +3913,237 @@ internal class _KeyedDecodingContainerBase { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal var allKeys: [Key] { + internal var allKeys: [CodingKey] { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func contains(_ key: Key) -> Bool { + internal func contains(_ key: K) -> Bool { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeNil(forKey key: Key) throws -> Bool { + internal func decodeNil(forKey key: K) throws -> Bool { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: Bool.Type, - forKey key: Key + forKey key: K ) throws -> Bool { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: String.Type, - forKey key: Key + forKey key: K ) throws -> String { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: Double.Type, - forKey key: Key + forKey key: K ) throws -> Double { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: Float.Type, - forKey key: Key + forKey key: K ) throws -> Float { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: Int.Type, - forKey key: Key + forKey key: K ) throws -> Int { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: Int8.Type, - forKey key: Key + forKey key: K ) throws -> Int8 { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: Int16.Type, - forKey key: Key + forKey key: K ) throws -> Int16 { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: Int32.Type, - forKey key: Key + forKey key: K ) throws -> Int32 { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: Int64.Type, - forKey key: Key + forKey key: K ) throws -> Int64 { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: UInt.Type, - forKey key: Key + forKey key: K ) throws -> UInt { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: UInt8.Type, - forKey key: Key + forKey key: K ) throws -> UInt8 { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: UInt16.Type, - forKey key: Key + forKey key: K ) throws -> UInt16 { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: UInt32.Type, - forKey key: Key + forKey key: K ) throws -> UInt32 { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: UInt64.Type, - forKey key: Key + forKey key: K ) throws -> UInt64 { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decode( + internal func decode( _ type: T.Type, - forKey key: Key + forKey key: K ) throws -> T { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: Bool.Type, - forKey key: Key + forKey key: K ) throws -> Bool? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: String.Type, - forKey key: Key + forKey key: K ) throws -> String? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: Double.Type, - forKey key: Key + forKey key: K ) throws -> Double? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: Float.Type, - forKey key: Key + forKey key: K ) throws -> Float? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: Int.Type, - forKey key: Key + forKey key: K ) throws -> Int? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: Int8.Type, - forKey key: Key + forKey key: K ) throws -> Int8? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: Int16.Type, - forKey key: Key + forKey key: K ) throws -> Int16? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: Int32.Type, - forKey key: Key + forKey key: K ) throws -> Int32? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: Int64.Type, - forKey key: Key + forKey key: K ) throws -> Int64? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: UInt.Type, - forKey key: Key + forKey key: K ) throws -> UInt? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: UInt8.Type, - forKey key: Key + forKey key: K ) throws -> UInt8? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: UInt16.Type, - forKey key: Key + forKey key: K ) throws -> UInt16? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: UInt32.Type, - forKey key: Key + forKey key: K ) throws -> UInt32? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: UInt64.Type, - forKey key: Key + forKey key: K ) throws -> UInt64? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func decodeIfPresent( + internal func decodeIfPresent( _ type: T.Type, - forKey key: Key + forKey key: K ) throws -> T? { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func nestedContainer( + internal func nestedContainer( keyedBy type: NestedKey.Type, - forKey key: Key + forKey key: K ) throws -> KeyedDecodingContainer { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func nestedUnkeyedContainer( - forKey key: Key + internal func nestedUnkeyedContainer( + forKey key: K ) throws -> UnkeyedDecodingContainer { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } @@ -4084,14 +4152,14 @@ internal class _KeyedDecodingContainerBase { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } - internal func superDecoder(forKey key: Key) throws -> Decoder { + internal func superDecoder(forKey key: K) throws -> Decoder { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } } internal final class _KeyedDecodingContainerBox< Concrete: KeyedDecodingContainerProtocol ->: _KeyedDecodingContainerBase { +>: _KeyedDecodingContainerBase { typealias Key = Concrete.Key internal var concrete: Concrete @@ -4104,238 +4172,306 @@ internal final class _KeyedDecodingContainerBox< return concrete.codingPath } - override var allKeys: [Key] { + override var allKeys: [CodingKey] { return concrete.allKeys } - override internal func contains(_ key: Key) -> Bool { + override internal func contains(_ key: K) -> Bool { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return concrete.contains(key) } - override internal func decodeNil(forKey key: Key) throws -> Bool { + override internal func decodeNil(forKey key: K) throws -> Bool { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeNil(forKey: key) } - override internal func decode( + override internal func decode( _ type: Bool.Type, - forKey key: Key + forKey key: K ) throws -> Bool { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(Bool.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: String.Type, - forKey key: Key + forKey key: K ) throws -> String { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(String.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: Double.Type, - forKey key: Key + forKey key: K ) throws -> Double { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(Double.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: Float.Type, - forKey key: Key + forKey key: K ) throws -> Float { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(Float.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: Int.Type, - forKey key: Key + forKey key: K ) throws -> Int { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(Int.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: Int8.Type, - forKey key: Key + forKey key: K ) throws -> Int8 { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(Int8.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: Int16.Type, - forKey key: Key + forKey key: K ) throws -> Int16 { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(Int16.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: Int32.Type, - forKey key: Key + forKey key: K ) throws -> Int32 { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(Int32.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: Int64.Type, - forKey key: Key + forKey key: K ) throws -> Int64 { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(Int64.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: UInt.Type, - forKey key: Key + forKey key: K ) throws -> UInt { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(UInt.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: UInt8.Type, - forKey key: Key + forKey key: K ) throws -> UInt8 { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(UInt8.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: UInt16.Type, - forKey key: Key + forKey key: K ) throws -> UInt16 { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(UInt16.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: UInt32.Type, - forKey key: Key + forKey key: K ) throws -> UInt32 { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(UInt32.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: UInt64.Type, - forKey key: Key + forKey key: K ) throws -> UInt64 { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(UInt64.self, forKey: key) } - override internal func decode( + override internal func decode( _ type: T.Type, - forKey key: Key + forKey key: K ) throws -> T { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decode(T.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: Bool.Type, - forKey key: Key + forKey key: K ) throws -> Bool? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(Bool.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: String.Type, - forKey key: Key + forKey key: K ) throws -> String? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(String.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: Double.Type, - forKey key: Key + forKey key: K ) throws -> Double? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(Double.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: Float.Type, - forKey key: Key + forKey key: K ) throws -> Float? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(Float.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: Int.Type, - forKey key: Key + forKey key: K ) throws -> Int? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(Int.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: Int8.Type, - forKey key: Key + forKey key: K ) throws -> Int8? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(Int8.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: Int16.Type, - forKey key: Key + forKey key: K ) throws -> Int16? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(Int16.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: Int32.Type, - forKey key: Key + forKey key: K ) throws -> Int32? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(Int32.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: Int64.Type, - forKey key: Key + forKey key: K ) throws -> Int64? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(Int64.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: UInt.Type, - forKey key: Key + forKey key: K ) throws -> UInt? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(UInt.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: UInt8.Type, - forKey key: Key + forKey key: K ) throws -> UInt8? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(UInt8.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: UInt16.Type, - forKey key: Key + forKey key: K ) throws -> UInt16? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(UInt16.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: UInt32.Type, - forKey key: Key + forKey key: K ) throws -> UInt32? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(UInt32.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: UInt64.Type, - forKey key: Key + forKey key: K ) throws -> UInt64? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(UInt64.self, forKey: key) } - override internal func decodeIfPresent( + override internal func decodeIfPresent( _ type: T.Type, - forKey key: Key + forKey key: K ) throws -> T? { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.decodeIfPresent(T.self, forKey: key) } - override internal func nestedContainer( + override internal func nestedContainer( keyedBy type: NestedKey.Type, - forKey key: Key + forKey key: K ) throws -> KeyedDecodingContainer { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.nestedContainer(keyedBy: NestedKey.self, forKey: key) } - override internal func nestedUnkeyedContainer( - forKey key: Key + override internal func nestedUnkeyedContainer( + forKey key: K ) throws -> UnkeyedDecodingContainer { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.nestedUnkeyedContainer(forKey: key) } @@ -4343,7 +4479,9 @@ internal final class _KeyedDecodingContainerBox< return try concrete.superDecoder() } - override internal func superDecoder(forKey key: Key) throws -> Decoder { + override internal func superDecoder(forKey key: K) throws -> Decoder { + assert(K.self == Key.self) + let key = unsafeBitCast(key, to: Key.self) return try concrete.superDecoder(forKey: key) } } From f378ad5df0d57b22d9c2f11346933c416982c691 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 13 May 2020 15:45:47 -0700 Subject: [PATCH 04/11] [CodeCompletion] Handle "KeyPath as function" thunk in SanitizeExpr rdar://problem/60982638 (cherry picked from commit 32bd37756eea4b47b98abdcbf519d919f8af59c4) --- lib/Sema/CSGen.cpp | 36 ++++++++++++++++++++++++++++++++ test/IDE/complete_call_arg.swift | 19 +++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 0e34b060438e7..c31a2ff45216c 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -914,6 +914,35 @@ namespace { }; } // end anonymous namespace +namespace { +// Check if \p E is a call expression to curried thunk of "KeyPath as function". +// i.e. '{ `$kp$` in { $0[keyPath: $kp$] } }(keypath)' +static bool isKeyPathCurriedThunkCallExpr(Expr *E) { + auto CE = dyn_cast(E); + if (!CE) + return false; + auto thunk = dyn_cast(CE->getFn()); + if (!thunk) + return false; + if (thunk->getParameters()->size() != 1 || + thunk->getParameters()->get(0)->getParameterName().str() != "$kp$") + return false; + + auto PE = dyn_cast(CE->getArg()); + if (!PE) + return false; + return isa(PE->getSubExpr()); +} + +// Extract the keypath expression from the curried thunk expression. +static Expr *extractKeyPathFromCurryThunkCall(Expr *E) { + assert(isKeyPathCurriedThunkCallExpr(E)); + auto call = cast(E); + auto arg = cast(call->getArg()); + return arg->getSubExpr(); +} +} // end anonymous namespace + namespace { class ConstraintGenerator : public ExprVisitor { @@ -3781,6 +3810,12 @@ namespace { continue; } + // Extract keypath from '{ `$kp$` in { $0[keyPath: $kp$] } }(keypath)' + if (isKeyPathCurriedThunkCallExpr(expr)) { + expr = extractKeyPathFromCurryThunkCall(expr); + continue; + } + // Restore '@autoclosure'd value. if (auto ACE = dyn_cast(expr)) { // This is only valid if the closure doesn't have parameters. @@ -3788,6 +3823,7 @@ namespace { expr = ACE->getSingleExpressionBody(); continue; } + llvm_unreachable("other AutoClosureExpr must be handled specially"); } // Remove any semantic expression injected by typechecking. diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index ba59d28f333d1..4e297bf18e338 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -104,6 +104,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TUPLEELEM_1 | %FileCheck %s -check-prefix=TUPLEELEM_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TUPLEELEM_2 | %FileCheck %s -check-prefix=TUPLEELEM_2 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYPATH_THUNK_BASE | %FileCheck %s -check-prefix=KEYPATH_THUNK_BASE + var i1 = 1 var i2 = 2 var oi1 : Int? @@ -829,3 +831,20 @@ func testTupleElement(arg: (SimpleEnum, SimpleEnum)) { testTupleElement(arg: (.foo, .bar, .#^TUPLEELEM_2^#)) // TUPLEELEM_2-NOT: Begin completions } + +func testKeyPathThunkInBase() { + struct TestKP { + var value: Int { 1 } + } + struct TestKPResult { + func testFunc(_ arg: SimpleEnum) {} + } + func foo(_ fn: (TestKP) -> Int) -> TestKPResult { TestKPResult() } + + foo(\.value).testFunc(.#^KEYPATH_THUNK_BASE^#) +// KEYPATH_THUNK_BASE: Begin completions, 3 items +// KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo +// KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar +// KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz +// KEYPATH_THUNK_BASE: End completions +} From e35083dfc71ab2c349a64a686715ac9bb3e4d824 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 13 May 2020 16:37:48 -0700 Subject: [PATCH 05/11] [CodeCompletion] Avoid re-typechcking pre-checked expressions in expression context analysis. They are simply not necessary. rdar://problem/60982638 (cherry picked from commit 3337d7b25b13e58007cdd18092aa859403119ba8) --- lib/IDE/ExprContextAnalysis.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 242627b7e7fa0..ff5034f10f527 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -379,12 +379,16 @@ static void collectPossibleCalleesByQualifiedLookup( tyExpr->getTypeLoc().setType(nullptr); } - auto baseTyOpt = getTypeOfCompletionContextExpr( - DC.getASTContext(), &DC, CompletionTypeCheckKind::Normal, baseExpr, ref); - if (!baseTyOpt) - return; - - auto baseTy = (*baseTyOpt)->getWithoutSpecifierType(); + Type baseTy = baseExpr->getType(); + if (!baseTy || baseTy->is()) { + auto baseTyOpt = getTypeOfCompletionContextExpr( + DC.getASTContext(), &DC, CompletionTypeCheckKind::Normal, baseExpr, + ref); + if (!baseTyOpt) + return; + baseTy = *baseTyOpt; + } + baseTy = baseTy->getWithoutSpecifierType(); if (!baseTy->getMetatypeInstanceType()->mayHaveMembers()) return; From ecea9aadabc81ad68b2998a97fe200a9cea644cd Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 11 May 2020 14:49:30 -0700 Subject: [PATCH 06/11] Improve diagnostic for keypath used without 'keyPath:' label --- lib/Sema/CSSimplify.cpp | 38 +++++++++++++++++++---- test/Sema/keypath_subscript_nolabel.swift | 35 +++++++++++++++++++++ 2 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 test/Sema/keypath_subscript_nolabel.swift diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 211890bfa72d3..a6fce6600f489 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5682,6 +5682,19 @@ static bool isForKeyPathSubscript(ConstraintSystem &cs, return false; } +static bool isForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs, + ConstraintLocator *locator) { + if (!locator || !locator->getAnchor()) + return false; + + if (auto *SE = dyn_cast(locator->getAnchor())) { + auto *indexExpr = SE->getIndex(); + return isa(indexExpr) && + isa(indexExpr->getSemanticsProvidingExpr()); + } + return false; +} + /// Determine whether all of the given candidate overloads /// found through conditional conformances of a given base type. /// This is useful to figure out whether it makes sense to @@ -5809,7 +5822,13 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, MemberLookupResult result; result.OverallResult = MemberLookupResult::HasResults; - if (isForKeyPathSubscript(*this, memberLocator)) { + // Add key path result. + // If we are including inaccessible members, check for the use of a keypath + // subscript without a `keyPath:` label. Add it to the result so that it + // can be caught by the missing argument label checking later. + if (isForKeyPathSubscript(*this, memberLocator) || + (isForKeyPathSubscriptWithoutLabel(*this, memberLocator) + && includeInaccessibleMembers)) { if (baseTy->isAnyObject()) { result.addUnviable( OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication), @@ -9694,14 +9713,21 @@ ConstraintSystem::addKeyPathApplicationRootConstraint(Type root, ConstraintLocat path[0].getKind() == ConstraintLocator::SubscriptMember) || (path.size() == 2 && path[1].getKind() == ConstraintLocator::KeyPathDynamicMember)); + auto indexTuple = dyn_cast(subscript->getIndex()); - if (!indexTuple || indexTuple->getNumElements() != 1) - return; - - auto keyPathExpr = dyn_cast(indexTuple->getElement(0)); + auto indexParen = dyn_cast(subscript->getIndex()); + // If a keypath subscript is used without the expected `keyPath:` label, + // continue with type-checking when attempting fixes so that it gets caught + // by the argument label checking. In such cases, the KeyPathExpr is contained + // in a ParenExpr, instead of a TupleExpr. + assert(((indexTuple && indexTuple->getNumElements() == 1) || indexParen) && + "Expected KeyPathExpr to be in either TupleExpr or ParenExpr"); + + auto keyPathExpr = dyn_cast( + indexTuple ? indexTuple->getElement(0) : indexParen->getSubExpr()); if (!keyPathExpr) return; - + auto typeVar = getType(keyPathExpr)->getAs(); if (!typeVar) return; diff --git a/test/Sema/keypath_subscript_nolabel.swift b/test/Sema/keypath_subscript_nolabel.swift new file mode 100644 index 0000000000000..ef1a135538b3b --- /dev/null +++ b/test/Sema/keypath_subscript_nolabel.swift @@ -0,0 +1,35 @@ +// RUN: %target-swift-frontend -typecheck -verify -primary-file %s +// [SR-12745] +// rdar://problem/62957095 +struct S1 { + var x : Int = 0 +} +var s1 = S1() +s1[\.x] = 10 // expected-error {{missing argument label 'keyPath:' in subscript}} {{4-4=keyPath: }} + +struct S2 { + var x : Int = 0 + subscript(_ v: Int) -> Int { 0 } +} +var s2 = S2() +s2[\.x] = 10 // expected-error {{missing argument label 'keyPath:' in subscript}} {{4-4=keyPath: }} + +struct S3 { + var x : Int = 0 + subscript(v v: KeyPath) -> Int { get { 0 } set(newValue) {} } +} +var s3 = S3() +// TODO(diagnostics): This should actually be a diagnostic that correctly identifies that in the presence +// of a missing label, there are two options for resolution: 'keyPath' and 'v:' and to offer the user +// a choice. +// Today, the ExprTypeChecker identifies the disjunction with two of these possibilities, but +// filters out some of the terms based on label mismatch (but not implicit keypath terms, for example). +// It should probably not do that. +s3[\.x] = 10 // expected-error {{missing argument label 'keyPath:' in subscript}} {{4-4=keyPath: }} + +struct S4 { + var x : Int = 0 + subscript(v: KeyPath) -> Int { get { 0 } set(newValue) {} } +} +var s4 = S4() +s4[\.x] = 10 // expected-error {{key path value type 'Int' cannot be converted to contextual type 'String'}} From 9077a87ac809b02a6a7ff1fcd107d1c4e7ce74d3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 14 May 2020 18:30:08 -0400 Subject: [PATCH 07/11] SILGen: Remove obsolete hack preventing emission of allocating init for unavailable initializer Fixes . --- lib/SILGen/SILGen.cpp | 7 ------- test/SILGen/Inputs/objc_init_unavailable.h | 6 ++++++ test/SILGen/objc_init_unavailable.swift | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 test/SILGen/Inputs/objc_init_unavailable.h create mode 100644 test/SILGen/objc_init_unavailable.swift diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 5004872a1192b..cc75929674201 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -955,13 +955,6 @@ void SILGenModule::emitConstructor(ConstructorDecl *decl) { if (isa(decl->getDeclContext())) return; - // Always-unavailable imported constructors are factory methods - // that have been imported as constructors and then hidden by an - // imported init method. - if (decl->hasClangNode() && - decl->getAttrs().isUnavailable(decl->getASTContext())) - return; - SILDeclRef constant(decl); DeclContext *declCtx = decl->getDeclContext(); diff --git a/test/SILGen/Inputs/objc_init_unavailable.h b/test/SILGen/Inputs/objc_init_unavailable.h new file mode 100644 index 0000000000000..b99fc40093c3f --- /dev/null +++ b/test/SILGen/Inputs/objc_init_unavailable.h @@ -0,0 +1,6 @@ +@import Foundation; + +@interface ClassWithUnavailableInit : NSObject + +- (instancetype)initWithBundleID:(NSString *)bundleID __attribute__((availability(macos, unavailable))); +@end diff --git a/test/SILGen/objc_init_unavailable.swift b/test/SILGen/objc_init_unavailable.swift new file mode 100644 index 0000000000000..3cc885ff775fc --- /dev/null +++ b/test/SILGen/objc_init_unavailable.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-emit-silgen(mock-sdk: %clang-importer-sdk) -enable-objc-interop -import-objc-header %S/Inputs/objc_init_unavailable.h %s | %FileCheck %s +// REQUIRES: objc_interop + +@available(macOS, unavailable) +public func callUnavailableInit(name: String) -> ClassWithUnavailableInit { + return ClassWithUnavailableInit(bundleID: name) +} + +// CHECK-LABEL: sil [ossa] @$s21objc_init_unavailable19callUnavailableInit4nameSo09ClassWitheF0CSS_tF : $@convention(thin) (@guaranteed String) -> @owned ClassWithUnavailableInit { +// CHECK: function_ref @$sSo24ClassWithUnavailableInitC8bundleIDABSgSSSg_tcfC : $@convention(method) (@owned Optional, @thick ClassWithUnavailableInit.Type) -> @owned Optional +// CHECK: return + +// CHECK-LABEL: sil shared [serializable] [ossa] @$sSo24ClassWithUnavailableInitC8bundleIDABSgSSSg_tcfC : $@convention(method) (@owned Optional, @thick ClassWithUnavailableInit.Type) -> @owned Optional { +// CHECK: function_ref @$sSo24ClassWithUnavailableInitC8bundleIDABSgSSSg_tcfcTO : $@convention(method) (@owned Optional, @owned ClassWithUnavailableInit) -> @owned Optional +// CHECK: return + +// CHECK-LABEL: sil shared [serializable] [thunk] [ossa] @$sSo24ClassWithUnavailableInitC8bundleIDABSgSSSg_tcfcTO : $@convention(method) (@owned Optional, @owned ClassWithUnavailableInit) -> @owned Optional { +// CHECK: objc_method %1 : $ClassWithUnavailableInit, #ClassWithUnavailableInit.init!initializer.foreign : (ClassWithUnavailableInit.Type) -> (String?) -> ClassWithUnavailableInit?, $@convention(objc_method) (Optional, @owned ClassWithUnavailableInit) -> @owned Optional +// CHECK: return \ No newline at end of file From 810069e89441df9bf557e0fdac5ecba4ee3fb5fb Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 15 May 2020 01:18:21 -0700 Subject: [PATCH 08/11] [ConstraintSystem] Detect and diagnose inability to infer type of closure parameter(s) Detect situation when it's impossible to determine types for closure parameters used in the body from the context. E.g. when a call closure is associated with refers to a missing member. ```swift struct S { } S.foo { a, b in } // `S` doesn't have static member `foo` let _ = { v in } // not enough context to infer type of `v` _ = .foo { v in } // base type for `.foo` couldn't be determined ``` Resolves: [SR-12815](https://bugs.swift.org/browse/SR-12815) Resolves: rdar://problem/63230293 --- include/swift/AST/DiagnosticsSema.def | 3 + lib/Sema/CSBindings.cpp | 20 +++-- lib/Sema/CSDiagnostics.cpp | 74 +++++++++++++++++++ lib/Sema/CSDiagnostics.h | 9 +++ lib/Sema/CSFix.cpp | 33 +++++++++ lib/Sema/CSFix.h | 19 ++++- lib/Sema/CSGen.cpp | 6 +- lib/Sema/CSSimplify.cpp | 1 + lib/Sema/ConstraintGraph.cpp | 4 + lib/Sema/ConstraintSystem.h | 4 + lib/Sema/TypeCheckConstraints.cpp | 8 ++ test/Constraints/closures.swift | 20 ++++- test/Constraints/diagnostics.swift | 5 +- test/Sema/diag_ambiguous_overloads.swift | 8 +- test/decl/typealias/generic.swift | 4 +- test/expr/closure/anonymous.swift | 4 +- test/expr/closure/basic.swift | 2 +- .../salvage-with-other-type-errors.swift | 4 +- .../0186-rdar46497155.swift | 3 +- 19 files changed, 201 insertions(+), 30 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index db3720db146c2..6b03828649b41 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -245,6 +245,9 @@ ERROR(no_candidates_match_result_type,none, "no '%0' candidates produce the expected contextual result type %1", (StringRef, Type)) +ERROR(cannot_infer_closure_parameter_type,none, + "unable to infer type of a closure parameter %0 in the current context", + (StringRef)) ERROR(cannot_infer_closure_type,none, "unable to infer closure type in the current context", ()) ERROR(cannot_infer_closure_result_type,none, diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 0782046b41413..2d76e6909691a 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -1090,27 +1090,25 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { // resolved and had to be bound to a placeholder "hole" type. cs.increaseScore(SK_Hole); + ConstraintFix *fix = nullptr; if (auto *GP = TypeVar->getImpl().getGenericParameter()) { auto path = dstLocator->getPath(); // Drop `generic parameter` locator element so that all missing // generic parameters related to the same path can be coalesced later. - auto *fix = DefaultGenericArgument::create( + fix = DefaultGenericArgument::create( cs, GP, cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back())); - if (cs.recordFix(fix)) - return true; + } else if (TypeVar->getImpl().isClosureParameterType()) { + fix = SpecifyClosureParameterType::create(cs, dstLocator); } else if (TypeVar->getImpl().isClosureResultType()) { - auto *fix = SpecifyClosureReturnType::create( - cs, TypeVar->getImpl().getLocator()); - if (cs.recordFix(fix)) - return true; + fix = SpecifyClosureReturnType::create(cs, dstLocator); } else if (srcLocator->getAnchor() && isa(srcLocator->getAnchor())) { - auto *fix = SpecifyObjectLiteralTypeImport::create( - cs, TypeVar->getImpl().getLocator()); - if (cs.recordFix(fix)) - return true; + fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator); } + + if (fix && cs.recordFix(fix)) + return true; } } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 89f77ec1f6257..687f70db90e47 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -41,6 +41,12 @@ using namespace swift; using namespace constraints; +static bool hasFixFor(const Solution &solution, ConstraintLocator *locator) { + return llvm::any_of(solution.Fixes, [&locator](const ConstraintFix *fix) { + return fix->getLocator() == locator; + }); +} + FailureDiagnostic::~FailureDiagnostic() {} bool FailureDiagnostic::diagnose(bool asNote) { @@ -6127,6 +6133,74 @@ bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() { return true; } +bool UnableToInferClosureParameterType::diagnoseAsError() { + auto *closure = castToExpr(getRawAnchor()); + + // Let's check whether this closure is an argument to + // a call which couldn't be properly resolved e.g. + // missing member or invalid contextual reference and + // if so let's not diagnose this problem because main + // issue here is inability to establish context for + // closure inference. + // + // TODO(diagnostics): Once we gain an ability to determine + // originating source of type holes this check could be + // significantly simplified. + { + auto &solution = getSolution(); + + // If there is a contextual mismatch associated with this + // closure, let's not diagnose any parameter type issues. + if (hasFixFor(solution, getConstraintLocator( + closure, LocatorPathElt::ContextualType()))) + return false; + + if (auto *parentExpr = findParentExpr(closure)) { + while (parentExpr && + (isa(parentExpr) || isa(parentExpr))) { + parentExpr = findParentExpr(parentExpr); + } + + if (parentExpr) { + // Missing or invalid member reference in call. + if (auto *AE = dyn_cast(parentExpr)) { + if (getType(AE->getFn())->isHole()) + return false; + } + + // Any fix anchored on parent expression makes it unnecessary + // to diagnose unability to infer parameter type because it's + // an indication that proper context couldn't be established to + // resolve the closure. + if (llvm::any_of(solution.Fixes, + [&parentExpr](const ConstraintFix *fix) -> bool { + return fix->getAnchor() == parentExpr; + })) + return false; + } + } + } + + auto paramIdx = getLocator() + ->castLastElementTo() + .getIndex(); + + auto *PD = closure->getParameters()->get(paramIdx); + + llvm::SmallString<16> id; + llvm::raw_svector_ostream OS(id); + + if (PD->isAnonClosureParam()) { + OS << "$" << paramIdx; + } else { + OS << "'" << PD->getParameterName() << "'"; + } + + auto loc = PD->isAnonClosureParam() ? getLoc() : PD->getLoc(); + emitDiagnosticAt(loc, diag::cannot_infer_closure_parameter_type, OS.str()); + return true; +} + bool UnableToInferClosureReturnType::diagnoseAsError() { auto *closure = castToExpr(getRawAnchor()); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 79a1586f5dc91..709bb55bfc6b1 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1973,6 +1973,15 @@ class MissingContextualBaseInMemberRefFailure final : public FailureDiagnostic { bool diagnoseAsError(); }; +class UnableToInferClosureParameterType final : public FailureDiagnostic { +public: + UnableToInferClosureParameterType(const Solution &solution, + ConstraintLocator *locator) + : FailureDiagnostic(solution, locator) {} + + bool diagnoseAsError(); +}; + class UnableToInferClosureReturnType final : public FailureDiagnostic { public: UnableToInferClosureReturnType(const Solution &solution, diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index dfb2d5b98eab2..0206d04c3ed99 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -22,6 +22,7 @@ #include "ConstraintSystem.h" #include "OverloadChoice.h" #include "swift/AST/Expr.h" +#include "swift/AST/ParameterList.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceManager.h" @@ -1226,6 +1227,38 @@ SpecifyBaseTypeForContextualMember *SpecifyBaseTypeForContextualMember::create( SpecifyBaseTypeForContextualMember(cs, member, locator); } +std::string SpecifyClosureParameterType::getName() const { + std::string name; + llvm::raw_string_ostream OS(name); + + auto *closure = cast(getAnchor()); + auto paramLoc = + getLocator()->castLastElementTo(); + + auto *PD = closure->getParameters()->get(paramLoc.getIndex()); + + OS << "specify type for parameter "; + if (PD->isAnonClosureParam()) { + OS << "$" << paramLoc.getIndex(); + } else { + OS << "'" << PD->getParameterName() << "'"; + } + + return OS.str(); +} + +bool SpecifyClosureParameterType::diagnose(const Solution &solution, + bool asNote) const { + UnableToInferClosureParameterType failure(solution, getLocator()); + return failure.diagnose(asNote); +} + +SpecifyClosureParameterType * +SpecifyClosureParameterType::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) SpecifyClosureParameterType(cs, locator); +} + bool SpecifyClosureReturnType::diagnose(const Solution &solution, bool asNote) const { UnableToInferClosureReturnType failure(solution, getLocator()); diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index f06255de30120..1bee47cc0054f 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -232,6 +232,10 @@ enum class FixKind : uint8_t { /// inferred and has to be specified explicitly. SpecifyBaseTypeForContextualMember, + /// Type of the closure parameter used in the body couldn't be inferred + /// and has to be specified explicitly. + SpecifyClosureParameterType, + /// Closure return type has to be explicitly specified because it can't be /// inferred in current context e.g. because it's a multi-statement closure. SpecifyClosureReturnType, @@ -251,7 +255,7 @@ enum class FixKind : uint8_t { /// A warning fix that allows a coercion to perform a force-cast. AllowCoercionToForceCast, - + /// Allow key path root type mismatch when applying a key path that has a /// root type not convertible to the type of the base instance. AllowKeyPathRootTypeMismatch, @@ -1706,6 +1710,19 @@ class SpecifyBaseTypeForContextualMember final : public ConstraintFix { create(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator); }; +class SpecifyClosureParameterType final : public ConstraintFix { + SpecifyClosureParameterType(ConstraintSystem &cs, ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::SpecifyClosureParameterType, locator) {} + +public: + std::string getName() const; + + bool diagnose(const Solution &solution, bool asNote = false) const; + + static SpecifyClosureParameterType *create(ConstraintSystem &cs, + ConstraintLocator *locator); +}; + class SpecifyClosureReturnType final : public ConstraintFix { SpecifyClosureReturnType(ConstraintSystem &cs, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::SpecifyClosureReturnType, locator) {} diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index c31a2ff45216c..6aaf4301dd68f 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2196,8 +2196,12 @@ namespace { auto declaredTy = param->getType(); externalType = CS.openUnboundGenericType(declaredTy, paramLoc); } else { + // Let's allow parameters which haven't been explicitly typed + // to become holes by default, this helps in situations like + // `foo { a in }` where `foo` doesn't exist. externalType = CS.createTypeVariable( - paramLoc, TVO_CanBindToInOut | TVO_CanBindToNoEscape); + paramLoc, + TVO_CanBindToInOut | TVO_CanBindToNoEscape | TVO_CanBindToHole); } closureParams.push_back(param->toFunctionParam(externalType)); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index a6fce6600f489..e84fe67d7c071 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -9603,6 +9603,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowTupleSplatForSingleParameter: case FixKind::AllowInvalidUseOfTrailingClosure: case FixKind::AllowNonClassTypeToConvertToAnyObject: + case FixKind::SpecifyClosureParameterType: case FixKind::SpecifyClosureReturnType: case FixKind::AddQualifierToAccessTopLevelName: llvm_unreachable("handled elsewhere"); diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index cae8cc158d758..1bbe186cc4068 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -1126,6 +1126,10 @@ bool ConstraintGraph::contractEdges() { if (isParamBindingConstraint && tyvar1->getImpl().canBindToInOut()) { bool isNotContractable = true; if (auto bindings = CS.getPotentialBindings(tyvar1)) { + // Holes can't be contracted. + if (bindings.IsHole) + continue; + for (auto &binding : bindings.Bindings) { auto type = binding.BindingType; isNotContractable = type.findIf([&](Type nestedType) -> bool { diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 83d8005334a98..c30c0a1f210a5 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -300,6 +300,10 @@ class TypeVariableType::Implementation { /// Determine whether this type variable represents a closure type. bool isClosureType() const; + /// Determine whether this type variable represents one of the + /// parameter types associated with a closure. + bool isClosureParameterType() const; + /// Determine whether this type variable represents a closure result type. bool isClosureResultType() const; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 956253ba2dc02..6ddb39a07e68c 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -90,6 +90,14 @@ bool TypeVariableType::Implementation::isClosureType() const { return isa(locator->getAnchor()) && locator->getPath().empty(); } +bool TypeVariableType::Implementation::isClosureParameterType() const { + if (!(locator && locator->getAnchor())) + return false; + + return isa(locator->getAnchor()) && + locator->isLastElement(); +} + bool TypeVariableType::Implementation::isClosureResultType() const { if (!(locator && locator->getAnchor())) return false; diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 2eacf3e1b4497..1b290fb44ca1e 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -504,7 +504,7 @@ struct S_3520 { func sr3520_set_via_closure(_ closure: (inout S, T) -> ()) {} // expected-note {{in call to function 'sr3520_set_via_closure'}} sr3520_set_via_closure({ $0.number1 = $1 }) // expected-error@-1 {{generic parameter 'S' could not be inferred}} -// expected-error@-2 {{generic parameter 'T' could not be inferred}} +// expected-error@-2 {{unable to infer type of a closure parameter $1 in the current context}} // SR-3073: UnresolvedDotExpr in single expression closure @@ -1017,3 +1017,21 @@ func overloaded_with_default_and_autoclosure(b: Int = 0, c: @escaping () -> T overloaded_with_default_and_autoclosure { 42 } // Ok overloaded_with_default_and_autoclosure(42) // Ok + +// SR-12815 - `error: type of expression is ambiguous without more context` in many cases where methods are missing +func sr12815() { + let _ = { a, b in } + // expected-error@-1 {{unable to infer type of a closure parameter 'a' in the current context}} + // expected-error@-2 {{unable to infer type of a closure parameter 'b' in the current context}} + + _ = .a { b in } // expected-error {{cannot infer contextual base in reference to member 'a'}} + + struct S {} + + func test(s: S) { + S.doesntExist { b in } // expected-error {{type 'S' has no member 'doesntExist'}} + s.doesntExist { b in } // expected-error {{value of type 'S' has no member 'doesntExist'}} + s.doesntExist1 { v in } // expected-error {{value of type 'S' has no member 'doesntExist1'}} + .doesntExist2() { $0 } + } +} diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 2e2fb2ab35813..c10f96ad1e126 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -255,8 +255,7 @@ struct Toe { let toenail: Nail // expected-error {{cannot find type 'Nail' in scope}} func clip() { - // TODO(diagnostics): Solver should stop once it has detected that `toenail` doesn't exist and report that. - toenail.inspect { x in // expected-error {{type of expression is ambiguous without more context}} + toenail.inspect { x in toenail.inspect { y in } } } @@ -297,7 +296,7 @@ func r18800223(_ i : Int) { } // Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back -_ = { $0 } // expected-error {{unable to infer closure type in the current context}} +_ = { $0 } // expected-error {{unable to infer type of a closure parameter $0 in the current context}} diff --git a/test/Sema/diag_ambiguous_overloads.swift b/test/Sema/diag_ambiguous_overloads.swift index 339ff22671bfa..10712fc3531c3 100644 --- a/test/Sema/diag_ambiguous_overloads.swift +++ b/test/Sema/diag_ambiguous_overloads.swift @@ -15,15 +15,15 @@ fe(.baz) // expected-error {{reference to member 'baz' cannot be resolved withou fe(.nope, .nyet) // expected-error {{type 'Int' has no member 'nope'}} // expected-error@-1 {{reference to member 'nyet' cannot be resolved without a contextual type}} -func fg(_ f: (T) -> T) -> Void {} // expected-note {{in call to function 'fg'}} -fg({x in x}) // expected-error {{generic parameter 'T' could not be inferred}} +func fg(_ f: (T) -> T) -> Void {} +fg({x in x}) // expected-error {{unable to infer type of a closure parameter 'x' in the current context}} struct S { - func f(_ i: (T) -> T, _ j: Int) -> Void {} // expected-note {{in call to function 'f'}} + func f(_ i: (T) -> T, _ j: Int) -> Void {} func f(_ d: (Double) -> Double) -> Void {} func test() -> Void { - f({x in x}, 2) // expected-error {{generic parameter 'T' could not be inferred}} + f({x in x}, 2) // expected-error {{unable to infer type of a closure parameter 'x' in the current context}} } func g(_ a: T, _ b: Int) -> Void {} diff --git a/test/decl/typealias/generic.swift b/test/decl/typealias/generic.swift index 561d9cdb6b193..805226a7f28b6 100644 --- a/test/decl/typealias/generic.swift +++ b/test/decl/typealias/generic.swift @@ -68,7 +68,7 @@ typealias E = Int // expected-note {{generic type 'E' declared here}} // expected-note@-1 {{'T1' declared as parameter to type 'E'}} // expected-note@-2 {{'T2' declared as parameter to type 'E'}} -typealias F = (T1) -> T2 // expected-note {{'T1' declared as parameter to type 'F'}} +typealias F = (T1) -> T2 // Type alias of type alias. typealias G = A @@ -94,7 +94,7 @@ let _ : D = D(a: 1, b: 2) let _ : F = { (a : Int) -> Int in a } // Infer the types of F -let _ : F = { a in a } // expected-error {{generic parameter 'T1' could not be inferred}} +let _ : F = { a in a } // expected-error {{unable to infer type of a closure parameter 'a' in the current context}} _ = MyType(a: "foo", b: 42) _ = A(a: "foo", b: 42) diff --git a/test/expr/closure/anonymous.swift b/test/expr/closure/anonymous.swift index f48c6e62ce4df..566fa0cc1bcc8 100644 --- a/test/expr/closure/anonymous.swift +++ b/test/expr/closure/anonymous.swift @@ -13,7 +13,7 @@ func takesIntArray(_: [Int]) { } func takesVariadicInt(_: (Int...) -> ()) { } func takesVariadicIntInt(_: (Int, Int...) -> ()) { } -func takesVariadicGeneric(_ f: (T...) -> ()) { } // expected-note {{in call to function 'takesVariadicGeneric'}} +func takesVariadicGeneric(_ f: (T...) -> ()) { } func variadic() { // These work @@ -32,7 +32,7 @@ func variadic() { // FIXME: Problem here is related to multi-statement closure body not being type-checked together with // enclosing context. We could have inferred `$0` to be `[Int]` if `let` was a part of constraint system. takesVariadicGeneric({let _: [Int] = $0}) - // expected-error@-1 {{generic parameter 'T' could not be inferred}} + // expected-error@-1 {{unable to infer type of a closure parameter $0 in the current context}} takesVariadicIntInt({_ = $0; takesIntArray($1)}) takesVariadicIntInt({_ = $0; let _: [Int] = $1}) diff --git a/test/expr/closure/basic.swift b/test/expr/closure/basic.swift index 82c7d91926b2c..16a9c794e79dc 100644 --- a/test/expr/closure/basic.swift +++ b/test/expr/closure/basic.swift @@ -26,7 +26,7 @@ func variadic() { _ = f(1, 2) _ = f(1, 3) - let D = { (Ss ...) in 1 } // expected-error{{'...' cannot be applied to a subpattern which is not explicitly typed}}, expected-error{{unable to infer closure type in the current context}} + let D = { (Ss ...) in 1 } // expected-error{{'...' cannot be applied to a subpattern which is not explicitly typed}}, expected-error{{unable to infer type of a closure parameter 'Ss' in the current context}} } // Closures with attributes in the parameter list. diff --git a/test/expr/unary/keypath/salvage-with-other-type-errors.swift b/test/expr/unary/keypath/salvage-with-other-type-errors.swift index e06c026159eff..56aeeb10982e9 100644 --- a/test/expr/unary/keypath/salvage-with-other-type-errors.swift +++ b/test/expr/unary/keypath/salvage-with-other-type-errors.swift @@ -34,7 +34,7 @@ extension A: K { struct B { let v: String - func f1(block: (T) -> E) -> B { // expected-note {{in call to function 'f1(block:)'}} + func f1(block: (T) -> E) -> B { return self } @@ -42,7 +42,7 @@ struct B { } } func f3() { - B(v: "").f1(block: { _ in }).f2(keyPath: \B.v) // expected-error{{}} + B(v: "").f1(block: { _ in }).f2(keyPath: \B.v) // expected-error{{unable to infer type of a closure parameter '_' in the current context}} } // SR-5375 diff --git a/validation-test/compiler_crashers_2_fixed/0186-rdar46497155.swift b/validation-test/compiler_crashers_2_fixed/0186-rdar46497155.swift index 9063623e56f9b..753d4086ceb50 100644 --- a/validation-test/compiler_crashers_2_fixed/0186-rdar46497155.swift +++ b/validation-test/compiler_crashers_2_fixed/0186-rdar46497155.swift @@ -20,10 +20,9 @@ struct E { func foo(arr: [E], other: P) -> Bool { return arr.compactMap { i in - // expected-error@-1 {{generic parameter 'ElementOfResult' could not be inferred}} var flag = false return try? i.getB(&flag) - }.compactMap { u -> P? in + }.compactMap { u -> P? in // expected-error {{nable to infer type of a closure parameter 'u' in the current context}} guard let a = try? u.foo() else { return nil } return a.value! }.contains { From 115237150bea38ba9cc981bf31592007ff045c97 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 15 May 2020 14:53:32 -0700 Subject: [PATCH 09/11] SILGen: Extend scope for evaluation in memberwise initializers to include initializer expressions. Catch any cleanups that get emitted while evaluating the initializer expression for a property. Fixes rdar://problem/63187509. --- lib/SILGen/SILGenConstructor.cpp | 3 ++- ...memberwise_init_temporary_allocations.swift | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 test/SILGen/memberwise_init_temporary_allocations.swift diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index dd6a0f8d842c9..596c6be33d2db 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -252,6 +252,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); RValue value; + FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); + // If it's memberwise initialized, do so now. if (field->isMemberwiseInitialized(/*preferDeclaredProperties=*/false)) { assert(elti != eltEnd && "number of args does not match number of fields"); @@ -276,7 +278,6 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, } // Cleanup after this initialization. - FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); SILValue v = maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs, std::move(value)) .forwardAsSingleStorageValue(SGF, fieldTy, Loc); diff --git a/test/SILGen/memberwise_init_temporary_allocations.swift b/test/SILGen/memberwise_init_temporary_allocations.swift new file mode 100644 index 0000000000000..cae60e17be803 --- /dev/null +++ b/test/SILGen/memberwise_init_temporary_allocations.swift @@ -0,0 +1,18 @@ +// RUN: %target-swift-emit-silgen -verify %s + +protocol P { var x: Int { get } } + +extension Int: P { var x: Int { return self } } + +// rdar://problem/63187509: Evaluating the variable initializer for `px` +// requires allocating a temporary stack slot for the address only value of +// `Butt.p`. Ensure that this gets cleaned up appropriately (which is asserted +// by the SIL verifier). +struct Butt { + static var p: P = 0 + + let px = Butt.p.x + + let y: Int +} + From 27a1d5b629500451612b6c5404bcd2ce298fb0e6 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 14 May 2020 19:50:28 -0700 Subject: [PATCH 10/11] [ASTPrinter] Don't print inferred opaque result type witness Opaque result type syntax is not usable except the declaration of itself. In other places, users need to let them inferred. If they are inferred associated type, they need to reffered by the name of the associated type. rdar://problem/59817674 (cherry picked from commit 29398b17372f5993f7bbb82e4c4c10a7788baa5c) --- lib/AST/Type.cpp | 22 +++++++----- test/IDE/complete_opaque_result.swift | 36 +++++++++++++++++++ .../CursorInfo/cursor_opaque_result.swift | 35 ++++++++++++++++++ 3 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 test/SourceKit/CursorInfo/cursor_opaque_result.swift diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 0f48d8def7543..892d1c1d41d84 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3519,24 +3519,28 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances, // Retrieve the type witness. auto witness = - conformance.getConcrete()->getTypeWitness(assocType, options); - if (!witness || witness->hasError()) + conformance.getConcrete()->getTypeWitnessAndDecl(assocType, options); + + auto witnessTy = witness.getWitnessType(); + if (!witnessTy || witnessTy->hasError()) return failed(); // This is a hacky feature allowing code completion to migrate to // using Type::subst() without changing output. if (options & SubstFlags::DesugarMemberTypes) { - if (auto *aliasType = - dyn_cast(witness.getPointer())) { - if (!aliasType->is()) - witness = aliasType->getSinglyDesugaredType(); - } + if (auto *aliasType = dyn_cast(witnessTy.getPointer())) + witnessTy = aliasType->getSinglyDesugaredType(); + + // Another hack. If the type witness is a opaque result type. They can + // only be referred using the name of the associated type. + if (witnessTy->is()) + witnessTy = witness.getWitnessDecl()->getDeclaredInterfaceType(); } - if (witness->is()) + if (witnessTy->is()) return failed(); - return witness; + return witnessTy; } return failed(); diff --git a/test/IDE/complete_opaque_result.swift b/test/IDE/complete_opaque_result.swift index ef7accb15c7b2..03a95395d1276 100644 --- a/test/IDE/complete_opaque_result.swift +++ b/test/IDE/complete_opaque_result.swift @@ -22,6 +22,9 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=POSTFIX_TestProtocol_DOT | %FileCheck %s -check-prefix=POSTFIX_TestProtocol_DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=POSTFIX_TestProtocol_NODOT | %FileCheck %s -check-prefix=POSTFIX_TestProtocol_NODOT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERRIDE_TestProtocol2 | %FileCheck %s -check-prefix=OVERRIDE_TestProtocol2 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=POSTFIX_ConcreteTestProtocol2 | %FileCheck %s -check-prefix=POSTFIX_ConcreteTestProtocol2 + protocol MyProtocol { associatedtype Mistery } @@ -200,3 +203,36 @@ func postfixExpr() { // POSTFIX_TestProtocol_NODOT-DAG: BuiltinOperator/None: = {#TestProtocol#}[#Void#]; name={{.*$}} // POSTFIX_TestProtocol_NODOT-DAG: Keyword[self]/CurrNominal: .self[#TestProtocol#]; name={{.*$}} // POSTFIX_TestProtocol_NODOT-DAG: End completions + +protocol TestProtocol2 { + associatedtype Assoc: Comparable + func foo() -> Assoc + func bar() -> Assoc + func baz(x: @autoclosure () -> Assoc) -> (Assoc) -> Assoc +} +extension TestProtocol2 { + func inExt() -> Assoc { fatalError() } +} +struct ConcreteTestProtocol2: TestProtocol2 { + func foo() -> some Comparable { 1 } + #^OVERRIDE_TestProtocol2^# +// OVERRIDE_TestProtocol2: Begin completions +// OVERRIDE_TestProtocol2-NOT: foo() +// OVERRIDE_TestProtocol2-NOT: inExt() +// OVERRIDE_TestProtocol2-DAG: Decl[InstanceMethod]/Super: func bar() -> Assoc {|}; +// OVERRIDE_TestProtocol2-DAG: Decl[InstanceMethod]/Super: func baz(x: @autoclosure () -> Assoc) -> (Assoc) -> Assoc {|}; +// OVERRIDE_TestProtocol2-DAG: Decl[AssociatedType]/Super: typealias Assoc = {#(Type)#}; +// OVERRIDE_TestProtocol2-NOT: foo() +// OVERRIDE_TestProtocol2-NOT: inExt() +// OVERRIDE_TestProtocol2: End completions +} +func testUseTestProtocol2(value: ConcreteTestProtocol2) { + value.#^POSTFIX_ConcreteTestProtocol2^# +// POSTFIX_ConcreteTestProtocol2: Begin completions +// POSTFIX_ConcreteTestProtocol2-DAG: Keyword[self]/CurrNominal: self[#ConcreteTestProtocol2#]; +// POSTFIX_ConcreteTestProtocol2-DAG: Decl[InstanceMethod]/CurrNominal: foo()[#Comparable#]; +// POSTFIX_ConcreteTestProtocol2-DAG: Decl[InstanceMethod]/Super: bar()[#ConcreteTestProtocol2.Assoc#]; +// POSTFIX_ConcreteTestProtocol2-DAG: Decl[InstanceMethod]/Super: baz({#x: ConcreteTestProtocol2.Assoc#})[#(ConcreteTestProtocol2.Assoc) -> ConcreteTestProtocol2.Assoc#]; +// POSTFIX_ConcreteTestProtocol2-DAG: Decl[InstanceMethod]/Super: inExt()[#ConcreteTestProtocol2.Assoc#]; +// POSTFIX_ConcreteTestProtocol2: End completions +} diff --git a/test/SourceKit/CursorInfo/cursor_opaque_result.swift b/test/SourceKit/CursorInfo/cursor_opaque_result.swift new file mode 100644 index 0000000000000..33030a3bc3c58 --- /dev/null +++ b/test/SourceKit/CursorInfo/cursor_opaque_result.swift @@ -0,0 +1,35 @@ +public protocol P { + associatedtype Assoc + func foo() -> Assoc +} +extension P { + func bar() -> Assoc { fatalError() } +} + +public struct MyStruct: P { + public func foo() -> some Comparable { 1 } +} +func test(value: MyStruct) { + value.foo() + value.bar() +} + +// RUN: %sourcekitd-test -req=cursor -pos=13:9 %s -- %s -module-name MyModule | %FileCheck --check-prefix=OPAQUE %s +// RUN: %sourcekitd-test -req=cursor -pos=14:9 %s -- %s -module-name MyModule | %FileCheck --check-prefix=ASSOC %s + +// OPAQUE: foo() +// OPAQUE-NEXT: s:8MyModule0A6StructV3fooQryF +// OPAQUE-NEXT: (MyStruct) -> () -> some Comparable +// OPAQUE-NEXT: $sQrycD +// OPAQUE-NEXT: $s8MyModule0A6StructVD +// OPAQUE-NEXT: public func foo() -> some Comparable +// OPAQUE-NEXT: public func foo() -> some Comparable + + +// ASSOC: bar() +// ASSOC-NEXT: s:8MyModule1PPAAE3bar5AssocQzyF +// ASSOC-NEXT: (Self) -> () -> Self.Assoc +// ASSOC-NEXT: $s5AssocQzycD +// ASSOC-NEXT: $s8MyModule0A6StructVD +// ASSOC-NEXT: func bar() -> Assoc +// ASSOC-NEXT: func bar() -> Assoc From 83697edfc937c69e2f1f3552232a0f966d707a61 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 15 May 2020 23:55:04 -0500 Subject: [PATCH 11/11] [5.3] Revert RangeSet additions (#31827) * Revert "test: move RangeSet test into validation-test" This reverts commit e9aaef4beaeba34f8d33b75659c8cc24307433dd. * Revert "Add RangeSet and discontiguous collection operations (#28161)" This reverts commit c6183ee71b89f760caf06dd108a8358c94a91006. --- .../StdlibCollectionUnittest/CMakeLists.txt | 1 - .../COWLoggingArray.swift | 123 ---- stdlib/public/core/CMakeLists.txt | 3 - stdlib/public/core/CollectionAlgorithms.swift | 141 +---- stdlib/public/core/DiscontiguousSlice.swift | 206 ------- stdlib/public/core/GroupInfo.json | 3 - stdlib/public/core/MutableCollection.swift | 153 +---- .../core/RangeReplaceableCollection.swift | 93 +-- stdlib/public/core/RangeSet.swift | 583 ------------------ stdlib/public/core/RangeSetStorage.swift | 176 ------ test/Constraints/members.swift | 17 +- test/type/types.swift | 18 +- validation-test/StdlibUnittest/RangeSet.swift | 363 ----------- 13 files changed, 17 insertions(+), 1863 deletions(-) delete mode 100644 stdlib/private/StdlibCollectionUnittest/COWLoggingArray.swift delete mode 100644 stdlib/public/core/DiscontiguousSlice.swift delete mode 100644 stdlib/public/core/RangeSet.swift delete mode 100644 stdlib/public/core/RangeSetStorage.swift delete mode 100644 validation-test/StdlibUnittest/RangeSet.swift diff --git a/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt b/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt index 0efaf5ab37f0b..451a7d296e940 100644 --- a/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt @@ -12,7 +12,6 @@ add_swift_target_library(swiftStdlibCollectionUnittest ${SWIFT_STDLIB_LIBRARY_BU CheckRangeReplaceableSliceType.swift CheckSequenceInstance.swift CheckSequenceType.swift - COWLoggingArray.swift LoggingWrappers.swift MinimalCollections.swift RangeSelection.swift diff --git a/stdlib/private/StdlibCollectionUnittest/COWLoggingArray.swift b/stdlib/private/StdlibCollectionUnittest/COWLoggingArray.swift deleted file mode 100644 index 70d89007e3daa..0000000000000 --- a/stdlib/private/StdlibCollectionUnittest/COWLoggingArray.swift +++ /dev/null @@ -1,123 +0,0 @@ -import StdlibUnittest - -fileprivate var COWLoggingArray_CopyCount = 0 - -public func expectNoCopyOnWrite( - _ elements: [T], - _ message: @autoclosure () -> String = "", - stackTrace: SourceLocStack = SourceLocStack(), - showFrame: Bool = true, - file: String = #file, - line: UInt = #line, - _ body: (inout COWLoggingArray) -> Void -) { - let copyCountBeforeBody = COWLoggingArray_CopyCount - var loggingArray = COWLoggingArray(elements) - body(&loggingArray) - expectEqual(copyCountBeforeBody, COWLoggingArray_CopyCount, message(), - stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), - showFrame: false) -} - -public struct COWLoggingArray { - var storage: Storage - - class Storage { - var buffer: UnsafeMutableBufferPointer - var count: Int - var capacity: Int { - buffer.count - } - - init(capacity: Int) { - self.buffer = .allocate(capacity: capacity) - self.count = 0 - } - - deinit { - buffer.baseAddress!.deinitialize(count: count) - buffer.deallocate() - } - - func cloned(capacity: Int? = nil) -> Storage { - let newCapacity = Swift.max(capacity ?? self.capacity, self.capacity) - let newStorage = Storage(capacity: newCapacity) - newStorage.buffer.baseAddress! - .initialize(from: buffer.baseAddress!, count: count) - newStorage.count = count - return newStorage - } - } - - mutating func _makeUnique() { - if !isKnownUniquelyReferenced(&storage) { - storage = storage.cloned() - COWLoggingArray_CopyCount += 1 - } - } -} - -extension COWLoggingArray: RandomAccessCollection, RangeReplaceableCollection, - MutableCollection, ExpressibleByArrayLiteral -{ - public var count: Int { storage.count } - public var startIndex: Int { 0 } - public var endIndex: Int { count } - - public subscript(i: Int) -> Element { - get { - storage.buffer[i] - } - set { - _makeUnique() - storage.buffer[i] = newValue - } - } - - public init() { - storage = Storage(capacity: 10) - } - - public mutating func reserveCapacity(_ n: Int) { - if !isKnownUniquelyReferenced(&storage) { - COWLoggingArray_CopyCount += 1 - storage = storage.cloned(capacity: n) - } else if count < n { - storage = storage.cloned(capacity: n) - } - } - - public mutating func replaceSubrange(_ subrange: Range, with newElements: C) - where C : Collection, Element == C.Element - { - _makeUnique() - let newCount = (count - subrange.count) + newElements.count - if newCount > storage.capacity { - storage = storage.cloned(capacity: newCount) - } - - let startOfSubrange = storage.buffer.baseAddress! + subrange.lowerBound - let endOfSubrange = startOfSubrange + subrange.count - let endOfNewElements = startOfSubrange + newElements.count - let countAfterSubrange = count - subrange.upperBound - - // clear out old elements - startOfSubrange.deinitialize(count: subrange.count) - - // move elements above subrange - endOfNewElements.moveInitialize(from: endOfSubrange, count: countAfterSubrange) - - // assign new elements - for (pointer, element) in zip(startOfSubrange..., newElements) { - pointer.initialize(to: element) - } - - // update count - storage.count = newCount - } - - public init(arrayLiteral elements: Element...) { - storage = Storage(capacity: elements.count) - replaceSubrange(0..<0, with: elements) - } -} diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 95ee7f07826a5..942f14939c439 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -207,13 +207,10 @@ set(SWIFTLIB_SOURCES Availability.swift CollectionDifference.swift CollectionOfOne.swift - DiscontiguousSlice.swift Diffing.swift Mirror.swift PlaygroundDisplay.swift CommandLine.swift - RangeSet.swift - RangeSetStorage.swift SliceBuffer.swift SIMDVector.swift UnfoldSequence.swift diff --git a/stdlib/public/core/CollectionAlgorithms.swift b/stdlib/public/core/CollectionAlgorithms.swift index 8bb9bd46a102b..7d7e5467369c7 100644 --- a/stdlib/public/core/CollectionAlgorithms.swift +++ b/stdlib/public/core/CollectionAlgorithms.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2018 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 @@ -209,70 +209,6 @@ extension BidirectionalCollection where Element: Equatable { } } -//===----------------------------------------------------------------------===// -// subranges(where:) / subranges(of:) -//===----------------------------------------------------------------------===// - -extension Collection { - /// Returns the indices of all the elements that match the given predicate. - /// - /// For example, you can use this method to find all the places that a - /// vowel occurs in a string. - /// - /// let str = "Fresh cheese in a breeze" - /// let vowels: Set = ["a", "e", "i", "o", "u"] - /// let allTheVowels = str.subranges(where: { vowels.contains($0) }) - /// // str[allTheVowels].count == 9 - /// - /// - Parameter predicate: A closure that takes an element as its argument - /// and returns a Boolean value that indicates whether the passed element - /// represents a match. - /// - Returns: A set of the indices of the elements for which `predicate` - /// returns `true`. - /// - /// - Complexity: O(*n*), where *n* is the length of the collection. - @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) - public func subranges(where predicate: (Element) throws -> Bool) rethrows - -> RangeSet - { - if isEmpty { return RangeSet() } - - var result = RangeSet() - var i = startIndex - while i != endIndex { - let next = index(after: i) - if try predicate(self[i]) { - result._append(i.. RangeSet { - subranges(where: { $0 == element }) - } -} - //===----------------------------------------------------------------------===// // partition(by:) //===----------------------------------------------------------------------===// @@ -433,81 +369,6 @@ extension MutableCollection where Self: BidirectionalCollection { } } -//===----------------------------------------------------------------------===// -// _indexedStablePartition / _partitioningIndex -//===----------------------------------------------------------------------===// - -extension MutableCollection { - /// Moves all elements at the indices satisfying `belongsInSecondPartition` - /// into a suffix of the collection, preserving their relative order, and - /// returns the start of the resulting suffix. - /// - /// - Complexity: O(*n* log *n*) where *n* is the number of elements. - /// - Precondition: - /// `n == distance(from: range.lowerBound, to: range.upperBound)` - internal mutating func _indexedStablePartition( - count n: Int, - range: Range, - by belongsInSecondPartition: (Index) throws-> Bool - ) rethrows -> Index { - if n == 0 { return range.lowerBound } - if n == 1 { - return try belongsInSecondPartition(range.lowerBound) - ? range.lowerBound - : range.upperBound - } - let h = n / 2, i = index(range.lowerBound, offsetBy: h) - let j = try _indexedStablePartition( - count: h, - range: range.lowerBound.. Bool - ) rethrows -> Index { - var n = count - var l = startIndex - - while n > 0 { - let half = n / 2 - let mid = index(l, offsetBy: half) - if try predicate(self[mid]) { - n = half - } else { - l = index(after: mid) - n -= half + 1 - } - } - return l - } -} - //===----------------------------------------------------------------------===// // shuffled()/shuffle() //===----------------------------------------------------------------------===// diff --git a/stdlib/public/core/DiscontiguousSlice.swift b/stdlib/public/core/DiscontiguousSlice.swift deleted file mode 100644 index eccd2c86e63dd..0000000000000 --- a/stdlib/public/core/DiscontiguousSlice.swift +++ /dev/null @@ -1,206 +0,0 @@ -//===--- DiscontiguousSlice.swift -----------------------------*- swift -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2020 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 -// -//===----------------------------------------------------------------------===// - -/// A collection wrapper that provides access to the elements of a collection, -/// indexed by a set of indices. -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -@frozen -public struct DiscontiguousSlice { - /// The collection that the indexed collection wraps. - public var base: Base - - /// The set of subranges that are available through this discontiguous slice. - public var subranges: RangeSet -} - -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -extension DiscontiguousSlice { - /// A position in an `DiscontiguousSlice`. - @frozen - public struct Index: Comparable { - /// The index of the range that contains `base`. - internal var _rangeOffset: Int - - /// The position of this index in the base collection. - public var base: Base.Index - - public static func < (lhs: Index, rhs: Index) -> Bool { - lhs.base < rhs.base - } - } -} - -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -extension DiscontiguousSlice.Index: Hashable where Base.Index: Hashable {} - -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -extension DiscontiguousSlice: Collection { - public typealias SubSequence = Self - - public var startIndex: Index { - subranges.isEmpty - ? endIndex - : Index(_rangeOffset: 0, base: subranges._ranges[0].lowerBound) - } - - public var endIndex: Index { - Index(_rangeOffset: subranges._ranges.endIndex, base: base.endIndex) - } - - public func index(after i: Index) -> Index { - let nextIndex = base.index(after: i.base) - if subranges._ranges[i._rangeOffset].contains(nextIndex) { - return Index(_rangeOffset: i._rangeOffset, base: nextIndex) - } - - let nextOffset = i._rangeOffset + 1 - if nextOffset < subranges._ranges.endIndex { - return Index( - _rangeOffset: nextOffset, - base: subranges._ranges[nextOffset].lowerBound) - } else { - return endIndex - } - } - - public subscript(i: Index) -> Base.Element { - base[i.base] - } - - public subscript(bounds: Range) -> DiscontiguousSlice { - let baseBounds = bounds.lowerBound.base ..< bounds.upperBound.base - let subset = subranges.intersection(RangeSet(baseBounds)) - return DiscontiguousSlice(base: base, subranges: subset) - } -} - -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -extension DiscontiguousSlice { - public var count: Int { - var c = 0 - for range in subranges._ranges { - c += base.distance(from: range.lowerBound, to: range.upperBound) - } - return c - } - - public __consuming func _copyToContiguousArray() -> ContiguousArray { - var result: ContiguousArray = [] - for range in subranges._ranges { - result.append(contentsOf: base[range]) - } - return result - } -} - -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -extension DiscontiguousSlice: BidirectionalCollection - where Base: BidirectionalCollection -{ - public func index(before i: Index) -> Index { - _precondition(i != startIndex, "Can't move index before startIndex") - - if i == endIndex || i.base == subranges._ranges[i._rangeOffset].lowerBound { - let offset = i._rangeOffset - 1 - return Index( - _rangeOffset: offset, - base: base.index(before: subranges._ranges[offset].upperBound)) - } - - return Index( - _rangeOffset: i._rangeOffset, - base: base.index(before: i.base)) - } -} - -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -extension DiscontiguousSlice: MutableCollection where Base: MutableCollection { - public subscript(i: Index) -> Base.Element { - get { - base[i.base] - } - set { - base[i.base] = newValue - } - } -} - -// MARK: Subscripts - -extension Collection { - /// Accesses a view of this collection with the elements at the given - /// indices. - /// - /// - Parameter subranges: The indices of the elements to retrieve from this - /// collection. - /// - Returns: A collection of the elements at the positions in `subranges`. - /// - /// - Complexity: O(1) - @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) - public subscript(subranges: RangeSet) -> DiscontiguousSlice { - DiscontiguousSlice(base: self, subranges: subranges) - } -} - -extension MutableCollection { - /// Accesses a mutable view of this collection with the elements at the - /// given indices. - /// - /// - Parameter subranges: The ranges of the elements to retrieve from this - /// collection. - /// - Returns: A collection of the elements at the positions in `subranges`. - /// - /// - Complexity: O(1) to access the elements, O(*m*) to mutate the - /// elements at the positions in `subranges`, where *m* is the number of - /// elements indicated by `subranges`. - @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) - public subscript(subranges: RangeSet) -> DiscontiguousSlice { - get { - DiscontiguousSlice(base: self, subranges: subranges) - } - set { - for i in newValue.indices { - self[i.base] = newValue[i] - } - } - } -} - -extension Collection { - /// Returns a collection of the elements in this collection that are not - /// represented by the given range set. - /// - /// For example, this code sample finds the indices of all the vowel - /// characters in the string, and then retrieves a collection that omits - /// those characters. - /// - /// let str = "The rain in Spain stays mainly in the plain." - /// let vowels: Set = ["a", "e", "i", "o", "u"] - /// let vowelIndices = str.subranges(where: { vowels.contains($0) }) - /// - /// let disemvoweled = str.removingSubranges(vowelIndices) - /// print(String(disemvoweled)) - /// // Prints "Th rn n Spn stys mnly n th pln." - /// - /// - Parameter subranges: A range set representing the indices of the - /// elements to remove. - /// - Returns: A collection of the elements that are not in `subranges`. - /// - /// - Complexity: O(*n*), where *n* is the length of the collection. - @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) - public func removingSubranges( - _ subranges: RangeSet - ) -> DiscontiguousSlice { - let inversion = subranges._inverted(within: self) - return self[inversion] - } -} diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index 1b9a209004e67..e08fa92d9fd03 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -70,8 +70,6 @@ "Sort.swift", "Range.swift", "ClosedRange.swift", - "RangeSet.swift", - "RangeSetStorage.swift", "CollectionOfOne.swift", "BridgingBuffer.swift", "Sequence.swift", @@ -98,7 +96,6 @@ "Filter.swift", "Reverse.swift", "Slice.swift", - "DiscontiguousSlice.swift", "DropWhile.swift", "PrefixWhile.swift", "LazyCollection.swift", diff --git a/stdlib/public/core/MutableCollection.swift b/stdlib/public/core/MutableCollection.swift index 3e335e6f9a775..3fdd4e6cdc85a 100644 --- a/stdlib/public/core/MutableCollection.swift +++ b/stdlib/public/core/MutableCollection.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -267,157 +267,6 @@ extension MutableCollection { } } -//===----------------------------------------------------------------------===// -// moveSubranges(_:to:) -//===----------------------------------------------------------------------===// - -extension MutableCollection { - /// Moves the elements in the given subranges to just before the element at - /// the specified index. - /// - /// This example finds all the uppercase letters in the array and then - /// moves them to between `"i"` and `"j"`. - /// - /// var letters = Array("ABCdeFGhijkLMNOp") - /// let uppercaseRanges = letters.subranges(where: { $0.isUppercase }) - /// let rangeOfUppercase = letters.moveSubranges(uppercaseRanges, to: 10) - /// // String(letters) == "dehiABCFGLMNOjkp" - /// // rangeOfUppercase == 4..<13 - /// - /// - Parameters: - /// - subranges: The subranges of the elements to move. - /// - insertionPoint: The index to use as the destination of the elements. - /// - Returns: The new bounds of the moved elements. - /// - /// - Complexity: O(*n* log *n*) where *n* is the length of the collection. - @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) - @discardableResult - public mutating func moveSubranges( - _ subranges: RangeSet, to insertionPoint: Index - ) -> Range { - let lowerCount = distance(from: startIndex, to: insertionPoint) - let upperCount = distance(from: insertionPoint, to: endIndex) - let start = _indexedStablePartition( - count: lowerCount, - range: startIndex.., - shiftingToStart middle: Index - ) -> Index { - var m = middle, s = subrange.lowerBound - let e = subrange.upperBound - - // Handle the trivial cases - if s == m { return e } - if m == e { return s } - - // We have two regions of possibly-unequal length that need to be - // exchanged. The return value of this method is going to be the - // position following that of the element that is currently last - // (element j). - // - // [a b c d e f g|h i j] or [a b c|d e f g h i j] - // ^ ^ ^ ^ ^ ^ - // s m e s m e - // - var ret = e // start with a known incorrect result. - while true { - // Exchange the leading elements of each region (up to the - // length of the shorter region). - // - // [a b c d e f g|h i j] or [a b c|d e f g h i j] - // ^^^^^ ^^^^^ ^^^^^ ^^^^^ - // [h i j d e f g|a b c] or [d e f|a b c g h i j] - // ^ ^ ^ ^ ^ ^ ^ ^ - // s s1 m m1/e s s1/m m1 e - // - let (s1, m1) = _swapNonemptySubrangePrefixes(s.., _ rhs: Range - ) -> (Index, Index) { - assert(!lhs.isEmpty) - assert(!rhs.isEmpty) - - var p = lhs.lowerBound - var q = rhs.lowerBound - repeat { - swapAt(p, q) - formIndex(after: &p) - formIndex(after: &q) - } while p != lhs.upperBound && q != rhs.upperBound - return (p, q) - } -} - // the legacy swap free function // /// Exchanges the values of the two arguments. diff --git a/stdlib/public/core/RangeReplaceableCollection.swift b/stdlib/public/core/RangeReplaceableCollection.swift index 0765d74f8e20a..ff520598bbde8 100644 --- a/stdlib/public/core/RangeReplaceableCollection.swift +++ b/stdlib/public/core/RangeReplaceableCollection.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -1138,94 +1138,3 @@ extension RangeReplaceableCollection { self = try filter { try !shouldBeRemoved($0) } } } - -extension RangeReplaceableCollection { - /// Removes the elements at the given indices. - /// - /// For example, this code sample finds the indices of all the vowel - /// characters in the string, and then removes those characters. - /// - /// var str = "The rain in Spain stays mainly in the plain." - /// let vowels: Set = ["a", "e", "i", "o", "u"] - /// let vowelIndices = str.subranges(where: { vowels.contains($0) }) - /// - /// str.removeSubranges(vowelIndices) - /// // str == "Th rn n Spn stys mnly n th pln." - /// - /// - Parameter subranges: The indices of the elements to remove. - /// - /// - Complexity: O(*n*), where *n* is the length of the collection. - @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) - public mutating func removeSubranges(_ subranges: RangeSet) { - guard !subranges.isEmpty else { - return - } - - let inversion = subranges._inverted(within: self) - var result = Self() - for range in inversion.ranges { - result.append(contentsOf: self[range]) - } - self = result - } -} - -extension MutableCollection where Self: RangeReplaceableCollection { - /// Removes the elements at the given indices. - /// - /// For example, this code sample finds the indices of all the negative - /// numbers in the array, and then removes those values. - /// - /// var numbers = [5, 7, -3, -8, 11, 2, -1, 6] - /// let negativeIndices = numbers.subranges(where: { $0 < 0 }) - /// - /// numbers.removeSubranges(negativeIndices) - /// // numbers == [5, 7, 11, 2, 6] - /// - /// - Parameter subranges: The indices of the elements to remove. - /// - /// - Complexity: O(*n*), where *n* is the length of the collection. - @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) - public mutating func removeSubranges(_ subranges: RangeSet) { - guard let firstRange = subranges.ranges.first else { - return - } - - var endOfElementsToKeep = firstRange.lowerBound - var firstUnprocessed = firstRange.upperBound - - // This performs a half-stable partition based on the ranges in - // `indices`. At all times, the collection is divided into three - // regions: - // - // - `self[.. { - internal var _ranges = _RangeSetStorage() - - /// Creates an empty range set. - public init() {} - - /// Creates a range set containing the given range. - /// - /// - Parameter range: The range to use for the new range set. - public init(_ range: Range) { - if !range.isEmpty { - self._ranges = _RangeSetStorage(range) - } - } - - /// Creates a range set containing the values in the given ranges. - /// - /// Any empty ranges in `ranges` are ignored, and non-empty ranges are merged - /// to eliminate any overlaps. As such, the `ranges` collection in the - /// resulting range set may not be equivalent to the sequence of ranges - /// passed to this initializer. - /// - /// - Parameter ranges: The ranges to use for the new range set. - public init(_ ranges: S) where S.Element == Range { - for range in ranges { - insert(contentsOf: range) - } - } - - /// Checks the invariants of `_ranges`. - /// - /// The ranges stored by a range set are never empty, never overlap, - /// and are always stored in ascending order when comparing their lower - /// or upper bounds. In addition to not overlapping, no two consecutive - /// ranges share an upper and lower bound — `[0..<5, 5..<10]` is ill-formed, - /// and would instead be represented as `[0..<10]`. - internal func _checkInvariants() { - for (a, b) in zip(ranges, ranges.dropFirst()) { - _debugPrecondition(!a.isEmpty && !b.isEmpty, "Empty range in range set") - _debugPrecondition( - a.upperBound < b.lowerBound, - "Out of order/overlapping ranges in range set") - } - } - - /// Creates a new range set from `ranges`, which satisfies the range set - /// invariants. - internal init(_orderedRanges ranges: [Range]) { - self._ranges = _RangeSetStorage(ranges) - _checkInvariants() - } - - /// A Boolean value indicating whether the range set is empty. - public var isEmpty: Bool { - _ranges.isEmpty - } - - /// Returns a Boolean value indicating whether the given value is - /// contained by the ranges in the range set. - /// - /// - Parameter value: The value to look for in the range set. - /// - Returns: `true` if `value` is contained by a range in the range set; - /// otherwise, `false`. - /// - /// - Complexity: O(log *n*), where *n* is the number of ranges in the - /// range set. - public func contains(_ value: Bound) -> Bool { - let i = _ranges._partitioningIndex { $0.upperBound > value } - return i == _ranges.endIndex - ? false - : _ranges[i].lowerBound <= value - } - - /// Returns a range indicating the existing ranges that `range` overlaps - /// with. - /// - /// For example, if `self` is `[0..<5, 10..<15, 20..<25, 30..<35]`, then: - /// - /// - `_indicesOfRange(12..<14) == 1..<2` - /// - `_indicesOfRange(12..<19) == 1..<2` - /// - `_indicesOfRange(17..<19) == 2..<2` - /// - `_indicesOfRange(12..<22) == 1..<3` - internal func _indicesOfRange(_ range: Range) -> Range { - _precondition(!range.isEmpty) - _precondition(!_ranges.isEmpty) - _precondition(range.lowerBound <= _ranges.last!.upperBound) - _precondition(range.upperBound >= _ranges.first!.lowerBound) - - // The beginning index for the position of `range` is the first range - // with an upper bound larger than `range`'s lower bound. The range - // at this position may or may not overlap `range`. - let beginningIndex = _ranges - ._partitioningIndex { $0.upperBound >= range.lowerBound } - - // The ending index for `range` is the first range with a lower bound - // greater than `range`'s upper bound. If this is the same as - // `beginningIndex`, than `range` doesn't overlap any of the existing - // ranges. If this is `ranges.endIndex`, then `range` overlaps the - // rest of the ranges. Otherwise, `range` overlaps one or - // more ranges in the set. - let endingIndex = _ranges[beginningIndex...] - ._partitioningIndex { $0.lowerBound > range.upperBound } - - return beginningIndex ..< endingIndex - } - - /// Inserts a non-empty range that is known to be greater than all the - /// elements in the set so far. - /// - /// - Precondition: The range set must be empty, or else - /// `ranges.last!.upperBound <= range.lowerBound`. - /// - Precondition: `range` must not be empty. - internal mutating func _append(_ range: Range) { - _precondition(_ranges.isEmpty - || _ranges.last!.upperBound <= range.lowerBound) - _precondition(!range.isEmpty) - if _ranges.isEmpty { - _ranges.append(range) - } else if _ranges.last!.upperBound == range.lowerBound { - _ranges[_ranges.count - 1] = - _ranges[_ranges.count - 1].lowerBound ..< range.upperBound - } else { - _ranges.append(range) - } - } - - /// Inserts the given range into the range set. - /// - /// - Parameter range: The range to insert into the set. - /// - /// - Complexity: O(*n*), where *n* is the number of ranges in the range - /// set. - public mutating func insert(contentsOf range: Range) { - // Shortcuts for the (literal) edge cases - if range.isEmpty { return } - guard !_ranges.isEmpty else { - _ranges.append(range) - return - } - guard range.lowerBound < _ranges.last!.upperBound else { - _append(range) - return - } - guard range.upperBound >= _ranges.first!.lowerBound else { - _ranges.insert(range, at: 0) - return - } - - let indices = _indicesOfRange(range) - - // Non-overlapping is a simple insertion. - guard !indices.isEmpty else { - _ranges.insert(range, at: indices.lowerBound) - return - } - - // Find the lower and upper bounds of the overlapping ranges. - let newLowerBound = Swift.min( - _ranges[indices.lowerBound].lowerBound, - range.lowerBound) - let newUpperBound = Swift.max( - _ranges[indices.upperBound - 1].upperBound, - range.upperBound) - _ranges.replaceSubrange( - indices, - with: CollectionOfOne(newLowerBound..) { - // Shortcuts for the (literal) edge cases - if range.isEmpty - || _ranges.isEmpty - || range.lowerBound >= _ranges.last!.upperBound - || range.upperBound < _ranges.first!.lowerBound - { return } - - let indices = _indicesOfRange(range) - - // No actual overlap, nothing to remove. - if indices.isEmpty { return } - - let overlapsLowerBound = - range.lowerBound > _ranges[indices.lowerBound].lowerBound - let overlapsUpperBound = - range.upperBound < _ranges[indices.upperBound - 1].upperBound - - switch (overlapsLowerBound, overlapsUpperBound) { - case (false, false): - _ranges.removeSubrange(indices) - case (false, true): - let newRange = - range.upperBound..<_ranges[indices.upperBound - 1].upperBound - _ranges.replaceSubrange(indices, with: CollectionOfOne(newRange)) - case (true, false): - let newRange = _ranges[indices.lowerBound].lowerBound.. - - public var startIndex: Int { _ranges.startIndex } - public var endIndex: Int { _ranges.endIndex } - - public subscript(i: Int) -> Range { - _ranges[i] - } - } - - /// A collection of the ranges that make up the range set. - /// - /// The ranges that you access by using `ranges` never overlap, are never - /// empty, and are always in increasing order. - public var ranges: Ranges { - Ranges(_ranges: _ranges) - } -} - -// MARK: - Collection APIs - -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -extension RangeSet { - /// Creates a new range set containing ranges that contain only the - /// specified indices in the given collection. - /// - /// - Parameters: - /// - index: The index to include in the range set. `index` must be a - /// valid index of `collection` that isn't the collection's `endIndex`. - /// - collection: The collection that contains `index`. - @inlinable - public init(_ indices: S, within collection: C) - where S: Sequence, C: Collection, S.Element == C.Index, C.Index == Bound - { - self.init() - for i in indices { - self.insert(i, within: collection) - } - } - - /// Inserts a range that contains only the specified index into the range - /// set. - /// - /// - Parameters: - /// - index: The index to insert into the range set. `index` must be a - /// valid index of `collection` that isn't the collection's `endIndex`. - /// - collection: The collection that contains `index`. - /// - /// - Complexity: O(*n*), where *n* is the number of ranges in the range - /// set. - @inlinable - public mutating func insert(_ index: Bound, within collection: C) - where C: Collection, C.Index == Bound - { - insert(contentsOf: index ..< collection.index(after: index)) - } - - /// Removes the range that contains only the specified index from the range - /// set. - /// - /// - Parameters: - /// - index: The index to remove from the range set. `index` must be a - /// valid index of `collection` that isn't the collection's `endIndex`. - /// - collection: The collection that contains `index`. - /// - /// - Complexity: O(*n*), where *n* is the number of ranges in the range - /// set. - @inlinable - public mutating func remove(_ index: Bound, within collection: C) - where C: Collection, C.Index == Bound - { - remove(contentsOf: index ..< collection.index(after: index)) - } - - /// Returns a range set that represents all the elements in the given - /// collection that aren't represented by this range set. - /// - /// - Parameter collection: The collection that the range set is relative - /// to. - /// - Returns: A new range set that represents the elements in `collection` - /// that aren't represented by this range set. - /// - /// - Complexity: O(*n*), where *n* is the number of ranges in the range - /// set. - internal func _inverted(within collection: C) -> RangeSet - where C: Collection, C.Index == Bound - { - return _gaps( - boundedBy: collection.startIndex..) -> RangeSet { - guard !_ranges.isEmpty else { return RangeSet(bounds) } - guard let start = _ranges.firstIndex(where: { $0.lowerBound >= bounds.lowerBound }) - else { return RangeSet() } - guard let end = _ranges.lastIndex(where: { $0.upperBound <= bounds.upperBound }) - else { return RangeSet() } - - var result = RangeSet() - var low = bounds.lowerBound - for range in _ranges[start...end] { - result.insert(contentsOf: low..) { - for range in other._ranges { - insert(contentsOf: range) - } - } - - /// Removes the contents of this range set that aren't also in the given - /// range set. - /// - /// - Parameter other: A range set to intersect with. - public mutating func formIntersection(_ other: RangeSet) { - self = self.intersection(other) - } - - /// Removes the contents of this range set that are also in the given set - /// and adds the contents of the given set that are not already in this - /// range set. - /// - /// - Parameter other: A range set to perform a symmetric difference against. - public mutating func formSymmetricDifference( - _ other: __owned RangeSet - ) { - self = self.symmetricDifference(other) - } - - /// Removes the contents of the given range set from this range set. - /// - /// - Parameter other: A range set to subtract from this one. - public mutating func subtract(_ other: RangeSet) { - for range in other._ranges { - remove(contentsOf: range) - } - } - - /// Returns a new range set containing the contents of both this set and the - /// given set. - /// - /// - Parameter other: The range set to merge with this one. - /// - Returns: A new range set. - public __consuming func union( - _ other: __owned RangeSet - ) -> RangeSet { - var result = self - result.formUnion(other) - return result - } - - /// Returns a new range set containing the contents of both this set and the - /// given set. - /// - /// - Parameter other: The range set to merge with this one. - /// - Returns: A new range set. - public __consuming func intersection( - _ other: RangeSet - ) -> RangeSet { - var otherRangeIndex = 0 - var result: [Range] = [] - - // Considering these two range sets: - // - // self = [0..<5, 9..<14] - // other = [1..<3, 4..<6, 8..<12] - // - // `self.intersection(other)` looks like this, where x's cover the - // ranges in `self`, y's cover the ranges in `other`, and z's cover the - // resulting ranges: - // - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - // xxxxxxxxxxxxxxxxxxx__ xxxxxxxxxxxxxxxxxxx__ - // yyyyyyy__ yyyyyyy__ yyyyyyyyyyyyyyy__ - // zzzzzzz__ zzz__ zzzzzzzzzzz__ - // - // The same, but for `other.intersection(self)`: - // - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - // xxxxxxx__ xxxxxxx__ xxxxxxxxxxxxxxx__ - // yyyyyyyyyyyyyyyyyyy__ yyyyyyyyyyyyyyyyyyy__ - // zzzzzzz__ zzz__ zzzzzzzzzzz__ - - for currentRange in _ranges { - // Search forward in `other` until finding either an overlapping - // range or one that is strictly higher than this range. - while otherRangeIndex < other._ranges.endIndex && - other._ranges[otherRangeIndex].upperBound <= currentRange.lowerBound - { - otherRangeIndex += 1 - } - - // For each range in `other` that overlaps with the current range - // in `self`, append the intersection to the result. - while otherRangeIndex < other._ranges.endIndex && - other._ranges[otherRangeIndex].lowerBound < currentRange.upperBound - { - let lower = Swift.max( - other._ranges[otherRangeIndex].lowerBound, - currentRange.lowerBound) - let upper = Swift.min( - other._ranges[otherRangeIndex].upperBound, - currentRange.upperBound) - result.append(lower.. other._ranges[otherRangeIndex].upperBound - else { - break - } - otherRangeIndex += 1 - } - } - - return RangeSet(_orderedRanges: result) - } - - /// Returns a new range set representing the values in this range set or the - /// given range set, but not both. - /// - /// - Parameter other: The range set to find a symmetric difference with. - /// - Returns: A new range set. - public __consuming func symmetricDifference( - _ other: __owned RangeSet - ) -> RangeSet { - return union(other).subtracting(intersection(other)) - } - - /// Returns a new set containing the contents of this range set that are not - /// also in the given range set. - /// - /// - Parameter other: The range set to subtract. - /// - Returns: A new range set. - public func subtracting(_ other: RangeSet) -> RangeSet { - var result = self - result.subtract(other) - return result - } - - /// Returns a Boolean value that indicates whether this range set is a - /// subset of the given set. - /// - /// - Parameter other: A range set to compare against. - /// - Returns: `true` if this range set is a subset of `other`; - /// otherwise, `false`. - public func isSubset(of other: RangeSet) -> Bool { - self.intersection(other) == self - } - - /// Returns a Boolean value that indicates whether this range set is a - /// superset of the given set. - /// - /// - Parameter other: A range set to compare against. - /// - Returns: `true` if this range set is a superset of `other`; - /// otherwise, `false`. - public func isSuperset(of other: RangeSet) -> Bool { - other.isSubset(of: self) - } - - /// Returns a Boolean value that indicates whether this range set is a - /// strict subset of the given set. - /// - /// - Parameter other: A range set to compare against. - /// - Returns: `true` if this range set is a strict subset of `other`; - /// otherwise, `false`. - public func isStrictSubset(of other: RangeSet) -> Bool { - self != other && isSubset(of: other) - } - - /// Returns a Boolean value that indicates whether this range set is a - /// strict superset of the given set. - /// - /// - Parameter other: A range set to compare against. - /// - Returns: `true` if this range set is a strict superset of `other`; - /// otherwise, `false`. - public func isStrictSuperset(of other: RangeSet) -> Bool { - other.isStrictSubset(of: self) - } -} - -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -extension RangeSet: CustomStringConvertible { - public var description: String { - let rangesDescription = _ranges - .map { r in "\(r.lowerBound)..<\(r.upperBound)" } - .joined(separator: ", ") - return "RangeSet(\(rangesDescription))" - } -} - -/// A collection of two elements, to avoid heap allocation when calling -/// `replaceSubrange` with just two elements. -internal struct _Pair: RandomAccessCollection { - internal var pair: (first: Element, second: Element) - - internal init(_ first: Element, _ second: Element) { - self.pair = (first, second) - } - - internal var startIndex: Int { 0 } - internal var endIndex: Int { 2 } - - internal subscript(position: Int) -> Element { - get { - switch position { - case 0: return pair.first - case 1: return pair.second - default: _preconditionFailure("Index is out of range") - } - } - } -} diff --git a/stdlib/public/core/RangeSetStorage.swift b/stdlib/public/core/RangeSetStorage.swift deleted file mode 100644 index 078b398cf54b1..0000000000000 --- a/stdlib/public/core/RangeSetStorage.swift +++ /dev/null @@ -1,176 +0,0 @@ -//===----------------------------------------------------------*- swift -*-===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2020 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 -// -//===----------------------------------------------------------------------===// - -/// Storage for a `RangeSet`. -/// -/// This is optimized to avoid allocating array storage for the common case of a -/// single range. The single range case is only attainable at initialization; -/// once added -internal struct _RangeSetStorage { - private enum _Storage { - case empty - case singleRange(Range) - case variadic([Range]) - } - - private var _storage: _Storage - - internal init() { - _storage = .empty - } - - internal init(_ range: Range) { - _storage = .singleRange(range) - } - - internal init(_ ranges: [Range]) { - _storage = .variadic(ranges) - } -} - -// _RangeSetStorage has custom Equatable (and therefore Hashable) -// conformance, since the same "value" can be represented by different -// storage structures. For example, `.empty` and `.variadic([])` are -// equivalent, but the synthesized conformance treats them as distinct. -// The same holds with the `singleRange` representation and `variadic` -// with a single-element array. - -extension _RangeSetStorage: Equatable { - internal static func == (lhs: _RangeSetStorage, rhs: _RangeSetStorage) -> Bool { - switch (lhs._storage, rhs._storage) { - case (.empty, .empty): - return true - case (.empty, .singleRange), (.singleRange, .empty): - return false - case let (.empty, .variadic(ranges)), - let (.variadic(ranges), .empty): - return ranges.isEmpty - - case let (.singleRange(lhs), .singleRange(rhs)): - return lhs == rhs - - case let (.singleRange(singleRange), .variadic(ranges)), - let (.variadic(ranges), .singleRange(singleRange)): - return ranges.count == 1 && - (ranges[0]) == singleRange - - case let (.variadic(lhsRanges), .variadic(rhsRanges)): - return lhsRanges == rhsRanges - } - } -} - -extension _RangeSetStorage: Hashable where T: Hashable { - internal func hash(into hasher: inout Hasher) { - for range in self { - hasher.combine(range) - } - } -} - -extension _RangeSetStorage: RandomAccessCollection, MutableCollection { - internal var startIndex: Int { 0 } - - internal var endIndex: Int { - switch _storage { - case .empty: return 0 - case .singleRange: return 1 - case let .variadic(ranges): return ranges.count - } - } - - internal subscript(i: Int) -> Range { - get { - switch _storage { - case .empty: - _preconditionFailure("Can't access elements of empty storage") - case let .singleRange(range): - _precondition(i == 0) - return range - case let .variadic(ranges): - return ranges[i] - } - } - set { - switch _storage { - case .empty: - _preconditionFailure("Can't access elements of empty storage") - case .singleRange: - _precondition(i == 0) - _storage = .singleRange(newValue) - case .variadic(var ranges): - // Temporarily set `_storage` to empty so that `ranges` - // remains uniquely referenced while mutating. - _storage = .empty - ranges[i] = newValue - _storage = .variadic(ranges) - } - } - } - - internal var count: Int { - switch _storage { - case .empty: return 0 - case .singleRange: return 1 - case let .variadic(ranges): return ranges.count - } - } -} - -extension _RangeSetStorage: RangeReplaceableCollection { - internal mutating func replaceSubrange( - _ subrange: Range, with newElements: C - ) where C : Collection, C.Element == Element { - switch _storage { - case .empty: - if !newElements.isEmpty { - _storage = .variadic(Array(newElements)) - } - - case let .singleRange(singleRange): - switch (subrange.isEmpty, newElements.isEmpty) { - case (false, true): - // Replacing the single range with an empty collection. - _storage = .empty - - case (false, false): - // Replacing the single range with a non-empty collection; - // promote to a variadic container. - _storage = .variadic(Array(newElements)) - - case (true, true): - // Inserting an empty collection; no-op. - break - - case (true, false): - // Inserting a non-empty collection either before or after - // the existing single element. - var ranges: [Range] - if subrange.lowerBound == 0 { - ranges = Array(newElements) - ranges.append(singleRange) - } else { - ranges = [singleRange] - ranges.append(contentsOf: newElements) - } - _storage = .variadic(ranges) - } - - case .variadic(var ranges): - // Temporarily set `_storage` to empty so that `ranges` - // remains uniquely referenced while mutating. - _storage = .empty - ranges.replaceSubrange(subrange, with: newElements) - _storage = .variadic(ranges) - } - } -} - diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index 54763e96a9c8b..8ed8b54f5b73f 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -615,16 +615,13 @@ func rdar50679161() { func rdar_50467583_and_50909555() { - if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { - // rdar://problem/50467583 - let _: Set = [Int][] // expected-error {{no exact matches in call to subscript}} - // expected-note@-1 {{found candidate with type '(Int) -> Int'}} - // expected-note@-2 {{found candidate with type '(Range) -> ArraySlice'}} - // expected-note@-3 {{found candidate with type '((UnboundedRange_) -> ()) -> ArraySlice'}} - // expected-note@-4 {{found candidate with type '(RangeSet.Index>) -> DiscontiguousSlice<[Int]>' (aka '(RangeSet) -> DiscontiguousSlice>')}} - // expected-note@-5 {{found candidate with type '(Range.Index>) -> Slice<[Int]>' (aka '(Range) -> Slice>')}} - } - + // rdar://problem/50467583 + let _: Set = [Int][] // expected-error {{no exact matches in call to subscript}} + // expected-note@-1 {{found candidate with type '(Int) -> Int'}} + // expected-note@-2 {{found candidate with type '(Range) -> ArraySlice'}} + // expected-note@-3 {{found candidate with type '((UnboundedRange_) -> ()) -> ArraySlice'}} + // expected-note@-4 {{found candidate with type '(Range.Index>) -> Slice<[Int]>' (aka '(Range) -> Slice>')}} + // rdar://problem/50909555 struct S { static subscript(x: Int, y: Int) -> Int { // expected-note {{'subscript(_:_:)' declared here}} diff --git a/test/type/types.swift b/test/type/types.swift index 7716a1582c3cf..e1a1115e7d363 100644 --- a/test/type/types.swift +++ b/test/type/types.swift @@ -18,17 +18,13 @@ var d3 : () -> Float = { 4 } var d4 : () -> Int = { d2 } // expected-error{{function produces expected type 'Int'; did you mean to call it with '()'?}} {{26-26=()}} -if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { - var e0 : [Int] - e0[] // expected-error {{no exact matches in call to subscript}} - // expected-note@-1 {{candidate has partially matching parameter list (Int)}} - // expected-note@-2 {{candidate has partially matching parameter list (Range)}} - // expected-note@-3 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} - // expected-note@-4 {{candidate has partially matching parameter list (RangeSet.Index>)}} - // expected-note@-5 {{candidate has partially matching parameter list (Range.Index>)}} - // expected-note@-6 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} - // expected-note@-7 {{candidate has partially matching parameter list (RangeSet.Index>)}} -} +var e0 : [Int] +e0[] // expected-error {{no exact matches in call to subscript}} +// expected-note@-1 {{candidate has partially matching parameter list (Int)}} +// expected-note@-2 {{candidate has partially matching parameter list (Range)}} +// expected-note@-3 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} +// expected-note@-4 {{candidate has partially matching parameter list (Range.Index>)}} +// expected-note@-5 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} var f0 : [Float] var f1 : [(Int,Int)] diff --git a/validation-test/StdlibUnittest/RangeSet.swift b/validation-test/StdlibUnittest/RangeSet.swift deleted file mode 100644 index 61bd605f80e4b..0000000000000 --- a/validation-test/StdlibUnittest/RangeSet.swift +++ /dev/null @@ -1,363 +0,0 @@ -// RUN: %target-run-simple-swift -// REQUIRES: executable_test - -import StdlibUnittest -import StdlibCollectionUnittest - -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) -extension RangeSet: ExpressibleByArrayLiteral { - public init(arrayLiteral elements: Range...) { - self.init(elements) - } -} - -extension Collection { - func every(_ n: Int) -> [Element] { - sequence(first: startIndex) { i in - self.index(i, offsetBy: n, limitedBy: self.endIndex) - }.map { self[$0] } - } -} - -let RangeSetTests = TestSuite("RangeSet") - -if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { - - let parent = -200..<200 - let source: RangeSet = [1..<5, 8..<10, 20..<22, 27..<29] - - let letterString = "ABCdefGHIjklMNOpqrStUvWxyz" - let lowercaseLetters = letterString.filter { $0.isLowercase } - let uppercaseLetters = letterString.filter { $0.isUppercase } - - func buildRandomRangeSet(iterations: Int = 100) -> RangeSet { - var set = RangeSet() - for _ in 0..<100 { - var (a, b) = (Int.random(in: -100...100), Int.random(in: -100...100)) - if (a > b) { swap(&a, &b) } - if Double.random(in: 0..<1) > 0.3 { - set.insert(contentsOf: a.., _ s2: RangeSet) -> RangeSet { - let set1 = Set(parent.indices[s1]) - let set2 = Set(parent.indices[s2]) - return RangeSet(set1.intersection(set2), within: parent) - } - - do { - // Simple test - let set1: RangeSet = [0..<5, 9..<14] - let set2: RangeSet = [1..<3, 4..<6, 8..<12] - let intersection: RangeSet = [1..<3, 4..<5, 9..<12] - expectEqual(set1.intersection(set2), intersection) - expectEqual(set2.intersection(set1), intersection) - } - - do { - // Test with upper bound / lower bound equality - let set1: RangeSet = [10..<20, 30..<40] - let set2: RangeSet = [15..<30, 40..<50] - let intersection: RangeSet = [15..<20] - expectEqual(set1.intersection(set2), intersection) - expectEqual(set2.intersection(set1), intersection) - } - - for _ in 0..<100 { - let set1 = buildRandomRangeSet() - let set2 = buildRandomRangeSet() - - let rangeSetIntersection = set1.intersection(set2) - let stdlibSetIntersection = intersectionViaSet(set1, set2) - expectEqual(rangeSetIntersection, stdlibSetIntersection) - } - } - - RangeSetTests.test("symmetricDifference") { - func symmetricDifferenceViaSet(_ s1: RangeSet, _ s2: RangeSet) -> RangeSet { - let set1 = Set(parent.indices[s1]) - let set2 = Set(parent.indices[s2]) - return RangeSet(set1.symmetricDifference(set2), within: parent) - } - - do { - // Simple test - let set1: RangeSet = [0..<5, 9..<14] - let set2: RangeSet = [1..<3, 4..<6, 8..<12] - let difference: RangeSet = [0..<1, 3..<4, 5..<6, 8..<9, 12..<14] - expectEqual(set1.symmetricDifference(set2), difference) - expectEqual(set2.symmetricDifference(set1), difference) - } - - do { - // Test with upper bound / lower bound equality - let set1: RangeSet = [10..<20, 30..<40] - let set2: RangeSet = [15..<30, 40..<50] - let difference: RangeSet = [10..<15, 20..<50] - expectEqual(set1.symmetricDifference(set2), difference) - expectEqual(set2.symmetricDifference(set1), difference) - } - - for _ in 0..<100 { - let set1 = buildRandomRangeSet() - let set2 = buildRandomRangeSet() - - let rangeSetDifference = set1.symmetricDifference(set2) - let stdlibSetDifference = symmetricDifferenceViaSet(set1, set2) - expectEqual(rangeSetDifference, stdlibSetDifference) - } - } - - RangeSetTests.test("subranges(of:/where:)") { - let a = [1, 2, 3, 4, 3, 3, 4, 5, 3, 4, 3, 3, 3] - let indices = a.subranges(of: 3) - expectEqual(indices, [2..<3, 4..<6, 8..<9, 10..<13]) - - let allTheThrees = a[indices] - expectEqual(allTheThrees.count, 7) - expectTrue(allTheThrees.allSatisfy { $0 == 3 }) - expectEqual(Array(allTheThrees), Array(repeating: 3, count: 7)) - - let lowerIndices = letterString.subranges(where: { $0.isLowercase }) - let lowerOnly = letterString[lowerIndices] - expectEqualSequence(lowerOnly, lowercaseLetters) - expectEqualSequence(lowerOnly.reversed(), lowercaseLetters.reversed()) - - let upperOnly = letterString.removingSubranges(lowerIndices) - expectEqualSequence(upperOnly, uppercaseLetters) - expectEqualSequence(upperOnly.reversed(), uppercaseLetters.reversed()) - } - - RangeSetTests.test("removeSubranges") { - var a = [1, 2, 3, 4, 3, 3, 4, 5, 3, 4, 3, 3, 3] - let indices = a.subranges(of: 3) - a.removeSubranges(indices) - expectEqual(a, [1, 2, 4, 4, 5, 4]) - - var numbers = Array(1...20) - numbers.removeSubranges(RangeSet([2..<5, 10..<15, 18..<20])) - expectEqual(numbers, [1, 2, 6, 7, 8, 9, 10, 16, 17, 18]) - - numbers = Array(1...20) - numbers.removeSubranges([]) - expectEqual(numbers, Array(1...20)) - - let sameNumbers = numbers.removingSubranges([]) - expectEqualSequence(numbers, sameNumbers) - - let noNumbers = numbers.removingSubranges(RangeSet(numbers.indices)) - expectEqualSequence(EmptyCollection(), noNumbers) - - var str = letterString - let lowerIndices = str.subranges(where: { $0.isLowercase }) - - let upperOnly = str.removingSubranges(lowerIndices) - expectEqualSequence(upperOnly, uppercaseLetters) - - str.removeSubranges(lowerIndices) - expectEqualSequence(str, uppercaseLetters) - } - - RangeSetTests.test("moveSubranges/rangeset") { - // Move before - var numbers = Array(1...20) - let range1 = numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 4) - expectEqual(range1, 4..<11) - expectEqual(numbers, [ - 1, 2, 3, 4, - 11, 12, 13, 14, 15, - 19, 20, - 5, 6, 7, 8, 9, 10, 16, 17, 18]) - - // Move to start - numbers = Array(1...20) - let range2 = numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 0) - expectEqual(range2, 0..<7) - expectEqual(numbers, [ - 11, 12, 13, 14, 15, - 19, 20, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16, 17, 18]) - - // Move to end - numbers = Array(1...20) - let range3 = numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 20) - expectEqual(range3, 13..<20) - expectEqual(numbers, [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16, 17, 18, - 11, 12, 13, 14, 15, - 19, 20, - ]) - - // Move to middle of selected elements - numbers = Array(1...20) - let range4 = numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 14) - expectEqual(range4, 10..<17) - expectEqual(numbers, [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, - 19, 20, - 16, 17, 18]) - - // Move none - numbers = Array(1...20) - let range5 = numbers.moveSubranges(RangeSet(), to: 10) - expectEqual(range5, 10..<10) - expectEqual(numbers, Array(1...20)) - } - - RangeSetTests.test("moveSubranges/noCOW") { - let numbers = Array(1...20) - expectNoCopyOnWrite(numbers) { numbers in - numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 4) - } - expectNoCopyOnWrite(numbers) { numbers in - numbers.removeSubranges(RangeSet([2..<5, 10..<15, 18..<20])) - } - } - - RangeSetTests.test("DiscontiguousSliceSlicing") { - let initial = 1...100 - - // Build an array of ranges that include alternating groups of 5 elements - // e.g. 1...5, 11...15, etc - let rangeStarts = initial.indices.every(10) - let rangeEnds = rangeStarts.compactMap { - initial.index($0, offsetBy: 5, limitedBy: initial.endIndex) - } - let ranges = zip(rangeStarts, rangeEnds).map(Range.init) - - // Create a collection of the elements represented by `ranges` without - // using `RangeSet` - let chosenElements = ranges.map { initial[$0] }.joined() - - let set = RangeSet(ranges) - let discontiguousSlice = initial[set] - expectEqualSequence(discontiguousSlice, chosenElements) - - for (chosenIdx, disIdx) in zip(chosenElements.indices, discontiguousSlice.indices) { - expectEqualSequence(chosenElements[chosenIdx...], discontiguousSlice[disIdx...]) - expectEqualSequence(chosenElements[..