diff --git a/lib/Sema/AssociatedTypeInference.cpp b/lib/Sema/AssociatedTypeInference.cpp index 4be7a3c94ba9c..bc4d8ea92e617 100644 --- a/lib/Sema/AssociatedTypeInference.cpp +++ b/lib/Sema/AssociatedTypeInference.cpp @@ -1017,6 +1017,10 @@ class AssociatedTypeInference { std::pair computeDerivedTypeWitness(AssociatedTypeDecl *assocType); + /// See if we have a generic parameter named the same as this associated + /// type. + Type computeGenericParamWitness(AssociatedTypeDecl *assocType) const; + /// Compute a type witness without using a specific potential witness. llvm::Optional computeAbstractTypeWitness(AssociatedTypeDecl *assocType); @@ -2389,6 +2393,7 @@ Type AssociatedTypeInference::computeFixedTypeWitness( // any fix this associated type to a concrete type. for (auto conformance : getPeerConformances(conformance)) { auto *conformedProto = conformance->getProtocol(); + auto sig = conformedProto->getGenericSignature(); // FIXME: The RequirementMachine will assert on re-entrant construction. @@ -2656,6 +2661,28 @@ AssociatedTypeInference::computeAbstractTypeWitness( return llvm::None; } +/// Look for a generic parameter that matches the name of the +/// associated type. +Type AssociatedTypeInference::computeGenericParamWitness( + AssociatedTypeDecl *assocType) const { + if (auto genericSig = dc->getGenericSignatureOfContext()) { + // Ignore the generic parameters for AsyncIteratorProtocol.Failure and + // AsyncSequence.Failure. + if (!isAsyncIteratorProtocolFailure(assocType)) { + for (auto *gp : genericSig.getInnermostGenericParams()) { + // Packs cannot witness associated type requirements. + if (gp->isParameterPack()) + continue; + + if (gp->getName() == assocType->getName()) + return dc->mapTypeIntoContext(gp); + } + } + } + + return Type(); +} + void AssociatedTypeInference::collectAbstractTypeWitnesses( TypeWitnessSystem &system, ArrayRef unresolvedAssocTypes) const { @@ -2704,35 +2731,15 @@ void AssociatedTypeInference::collectAbstractTypeWitnesses( if (system.hasResolvedTypeWitness(assocType->getName())) continue; - // If we find a default type definition, feed it to the system. - if (const auto &typeWitness = computeDefaultTypeWitness(assocType)) { + if (auto gpType = computeGenericParamWitness(assocType)) { + system.addTypeWitness(assocType->getName(), gpType, /*preferred=*/true); + } else if (const auto &typeWitness = computeDefaultTypeWitness(assocType)) { bool preferred = (typeWitness->getDefaultedAssocType()->getDeclContext() == conformance->getProtocol()); system.addDefaultTypeWitness(typeWitness->getType(), typeWitness->getDefaultedAssocType(), preferred); - } else { - // As a last resort, look for a generic parameter that matches the name - // of the associated type. - if (auto genericSig = dc->getGenericSignatureOfContext()) { - // Ignore the generic parameters for AsyncIteratorProtocol.Failure and - // AsyncSequence.Failure. - if (!isAsyncIteratorProtocolFailure(assocType)) { - for (auto *gp : genericSig.getInnermostGenericParams()) { - // Packs cannot witness associated type requirements. - if (gp->isParameterPack()) - continue; - - if (gp->getName() == assocType->getName()) { - system.addTypeWitness(assocType->getName(), - dc->mapTypeIntoContext(gp), - /*preferred=*/true); - } - } - } - } } - } } @@ -3150,8 +3157,15 @@ AssociatedTypeDecl *AssociatedTypeInference::inferAbstractTypeWitnesses( // If simplification failed, give up. if (type->hasTypeParameter()) { - LLVM_DEBUG(llvm::dbgs() << "-- Simplification failed: " << type << "\n"); - return assocType; + if (auto gpType = computeGenericParamWitness(assocType)) { + LLVM_DEBUG(llvm::dbgs() << "-- Found generic parameter as last resort: " + << gpType << "\n"); + type = gpType; + typeWitnesses.insert(assocType, {type, reqDepth}); + } else { + LLVM_DEBUG(llvm::dbgs() << "-- Simplification failed: " << type << "\n"); + return assocType; + } } if (const auto failed = diff --git a/test/decl/protocol/req/associated_type_generic_param.swift b/test/decl/protocol/req/associated_type_generic_param.swift index bfb44c3fe0f42..0a29eb1fd1bd2 100644 --- a/test/decl/protocol/req/associated_type_generic_param.swift +++ b/test/decl/protocol/req/associated_type_generic_param.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference -// RUN: %target-typecheck-verify-swift -disable-experimental-associated-type-inference +// RUN: not %target-typecheck-verify-swift -disable-experimental-associated-type-inference protocol P { associatedtype A = Int @@ -7,5 +7,4 @@ protocol P { struct S: P {} -// This is unfortunate but it is the behavior of Swift 5.10. -let x: Int.Type = S.A.self +let x: String.Type = S.A.self diff --git a/test/decl/protocol/req/associated_type_inference_fixed_type_experimental_inference.swift b/test/decl/protocol/req/associated_type_inference_fixed_type_experimental_inference.swift index fcee15a435c73..ce3ac5fc50063 100644 --- a/test/decl/protocol/req/associated_type_inference_fixed_type_experimental_inference.swift +++ b/test/decl/protocol/req/associated_type_inference_fixed_type_experimental_inference.swift @@ -212,7 +212,7 @@ do { // CHECK-NEXT: } struct Conformer1: P17a {} // expected-error {{type 'Conformer1' does not conform to protocol 'P17a'}} // CHECK-LABEL: Abstract type witness system for conformance of Conformer2 to P17b: { - // CHECK-NEXT: A => (unresolved), [[EQUIV_CLASS:0x[0-9a-f]+]] + // CHECK-NEXT: A => A (preferred), [[EQUIV_CLASS:0x[0-9a-f]+]] // CHECK-NEXT: B => (unresolved) // CHECK-NEXT: } struct Conformer2: P17b {} // expected-error {{type 'Conformer2' does not conform to protocol 'P17b'}} @@ -221,7 +221,7 @@ do { // CHECK-NEXT: } struct Conformer3: P17c {} // CHECK-LABEL: Abstract type witness system for conformance of Conformer4 to P17d: { - // CHECK-NEXT: A => Int (preferred), [[EQUIV_CLASS:0x[0-9a-f]+]] + // CHECK-NEXT: A => A (preferred), [[EQUIV_CLASS:0x[0-9a-f]+]] // CHECK-NEXT: B => Int (preferred), [[EQUIV_CLASS:0x[0-9a-f]+]] // CHECK-NEXT: } struct Conformer4: P17d {} diff --git a/test/decl/protocol/req/associated_type_inference_foundation_1.swift b/test/decl/protocol/req/associated_type_inference_foundation_1.swift new file mode 100644 index 0000000000000..f7498179c176e --- /dev/null +++ b/test/decl/protocol/req/associated_type_inference_foundation_1.swift @@ -0,0 +1,33 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference +// RUN: %target-typecheck-verify-swift -disable-experimental-associated-type-inference + +// REQUIRES: objc_interop + +import Foundation + +public struct CustomCollection: RandomAccessCollection { + public typealias Indices = Range + + public var startIndex: Int { fatalError() } + public var endIndex: Int { fatalError() } + public var count: Int { fatalError() } + + public subscript(position: Int) -> T { + get { fatalError() } + set { fatalError() } + } + + public var underestimatedCount: Int { fatalError() } +} + +extension CustomCollection: ContiguousBytes where Element == UInt8 { + public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { + fatalError() + } +} + +extension CustomCollection: DataProtocol where Element == UInt8 { + public var regions: CollectionOfOne> { + fatalError() + } +} diff --git a/test/decl/protocol/req/associated_type_inference_foundation_2.swift b/test/decl/protocol/req/associated_type_inference_foundation_2.swift new file mode 100644 index 0000000000000..8bb23dd01b1b7 --- /dev/null +++ b/test/decl/protocol/req/associated_type_inference_foundation_2.swift @@ -0,0 +1,45 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference +// RUN: %target-typecheck-verify-swift -disable-experimental-associated-type-inference + +// REQUIRES: objc_interop + +import Foundation + +public struct CustomCollection: RandomAccessCollection, MutableCollection, ExpressibleByArrayLiteral { + public typealias Indices = Range + public typealias Index = Int + + public init() {} + + public init(arrayLiteral elements: Element ...) { + fatalError() + } +} + +extension CustomCollection { + public var startIndex: Int { fatalError() } + public var endIndex: Int { fatalError() } + + public subscript(position: Int) -> Element { + get { fatalError() } + set { fatalError() } + } +} + +extension CustomCollection: RangeReplaceableCollection { + public mutating func append(_ newElement: Element) { } + public mutating func append(contentsOf newElements: S) where S.Element == Element { } + public mutating func reserveCapacity(_ minimumCapacity: Int) { } + public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) { } + public mutating func replaceSubrange(_ subRange: Range, with newElements: C) where C.Element == Element { } +} + +extension CustomCollection { + public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { fatalError() } +} + +extension CustomCollection: ContiguousBytes where Element == UInt8 { } + +extension CustomCollection: DataProtocol where Element == UInt8 { + public var regions: CollectionOfOne> { fatalError() } +} diff --git a/test/decl/protocol/req/associated_type_inference_stdlib_3.swift b/test/decl/protocol/req/associated_type_inference_stdlib_3.swift index 4eaf78b1e90a3..274197f17ba72 100644 --- a/test/decl/protocol/req/associated_type_inference_stdlib_3.swift +++ b/test/decl/protocol/req/associated_type_inference_stdlib_3.swift @@ -1,8 +1,6 @@ -// RUN: not %target-typecheck-verify-swift -enable-experimental-associated-type-inference +// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference // RUN: not %target-typecheck-verify-swift -disable-experimental-associated-type-inference -// FIXME: Get this passing with -enable-experimental-associated-type-inference again. - struct FooIterator: IteratorProtocol { typealias Element = T.Element diff --git a/test/decl/protocol/req/rdar123261282.swift b/test/decl/protocol/req/rdar123261282.swift new file mode 100644 index 0000000000000..1d30564208786 --- /dev/null +++ b/test/decl/protocol/req/rdar123261282.swift @@ -0,0 +1,21 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference +// RUN: %target-typecheck-verify-swift -disable-experimental-associated-type-inference + +struct CustomCollection: RandomAccessCollection { + struct CustomIndices: RandomAccessCollection { + var count: Int { fatalError() } + + var startIndex: Int { fatalError() } + var endIndex: Int { fatalError() } + + subscript(index: Int) -> Int { fatalError() } + } + + var count: Int { fatalError() } + var indices: CustomIndices { fatalError() } + var startIndex: Int { fatalError() } + var endIndex: Int { fatalError() } + func index(before i: Int) -> Int { fatalError() } + func index(after i: Int) -> Int { fatalError() } + subscript(index: Int) -> T { fatalError() } +} diff --git a/test/decl/protocol/req/rdar123262178.swift b/test/decl/protocol/req/rdar123262178.swift new file mode 100644 index 0000000000000..c2825caa07194 --- /dev/null +++ b/test/decl/protocol/req/rdar123262178.swift @@ -0,0 +1,14 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference +// RUN: %target-typecheck-verify-swift -disable-experimental-associated-type-inference + +public protocol P { + associatedtype A = Never +} + +public struct G: P {} + +public struct ConcreteA {} + +public protocol Q: P where Self.A == ConcreteA {} + +extension G: Q where A == ConcreteA {} diff --git a/test/decl/protocol/req/rdar123270042.swift b/test/decl/protocol/req/rdar123270042.swift new file mode 100644 index 0000000000000..32b88cbb87523 --- /dev/null +++ b/test/decl/protocol/req/rdar123270042.swift @@ -0,0 +1,28 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference +// RUN: not %target-typecheck-verify-swift -disable-experimental-associated-type-inference + +func f(_: T) -> T.Element {} + +let x: Int = f(CustomCollection()) + +struct CustomCollection: RandomAccessCollection, MutableCollection { + typealias SubSequence = ArraySlice + typealias Index = Int + typealias Indices = Range + + var startIndex: Int { fatalError() } + var endIndex: Int { fatalError() } + var first: Element? { fatalError() } + var last: Element? { fatalError() } + + subscript(position: Index) -> Element { + get { fatalError() } + set { fatalError() } + } + + subscript(bounds: Indices) -> SubSequence { + get { fatalError() } + set { fatalError() } + } +} + diff --git a/test/decl/protocol/req/rdar123345520.swift b/test/decl/protocol/req/rdar123345520.swift new file mode 100644 index 0000000000000..26ec1fa489fde --- /dev/null +++ b/test/decl/protocol/req/rdar123345520.swift @@ -0,0 +1,28 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference +// RUN: %target-typecheck-verify-swift -disable-experimental-associated-type-inference + +struct G1 {} + +struct G2: AP { + func f1(_: G1<(B) -> A>, _: G1) -> G1 { fatalError() } + func f2(_: (A) -> C) -> G1 { fatalError() } +} + +protocol OP: EP { + associatedtype L + associatedtype R + + func f1(_: G1, _: G1) -> G1 +} + +extension OP { + func f1(_: G1?, _: G1?) -> G1 { fatalError() } +} + +protocol AP: OP where L == (B) -> A, R == B {} + +protocol EP { + associatedtype A + associatedtype B + func f2(_: (A) -> C) -> G1 +}