Skip to content

Commit c07d5b5

Browse files
committed
AST: Fix caching policy in SpecializedProtocolConformance::getTypeWitnessAndDecl()
The substituted witness type may depend on a tentative type witness indirectly. In this case, we would incorrectly cache the result. The outcome was that associated type inference would fail because the first attempted witness would "lock in" a cached substituted witness in some other specialized conformance.
1 parent 0ecc8ef commit c07d5b5

File tree

2 files changed

+36
-22
lines changed

2 files changed

+36
-22
lines changed

lib/AST/ProtocolConformance.cpp

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -789,24 +789,13 @@ SpecializedProtocolConformance::getTypeWitnessAndDecl(
789789
assert(getProtocol() == cast<ProtocolDecl>(assocType->getDeclContext()) &&
790790
"associated type in wrong protocol");
791791

792-
// If we've already created this type witness, return it.
792+
// If we've already computed this type witness, return it.
793793
auto known = TypeWitnesses.find(assocType);
794794
if (known != TypeWitnesses.end()) {
795795
return known->second;
796796
}
797797

798-
// Otherwise, perform substitutions to create this witness now.
799-
800-
// Local function to determine whether we will end up referring to a
801-
// tentative witness that may not be chosen.
802-
auto root = GenericConformance->getRootConformance();
803-
auto isTentativeWitness = [&] {
804-
if (root->getState() != ProtocolConformanceState::CheckingTypeWitnesses)
805-
return false;
806-
807-
return !root->hasTypeWitness(assocType);
808-
};
809-
798+
// Otherwise, perform substitutions to compute this witness.
810799
auto genericWitnessAndDecl
811800
= GenericConformance->getTypeWitnessAndDecl(assocType, options);
812801

@@ -816,24 +805,18 @@ SpecializedProtocolConformance::getTypeWitnessAndDecl(
816805

817806
auto *typeDecl = genericWitnessAndDecl.getWitnessDecl();
818807

819-
// Form the substitution.
820808
auto substitutionMap = getSubstitutionMap();
821-
if (substitutionMap.empty())
822-
return TypeWitnessAndDecl();
823-
824-
// Apply the substitution we computed above
825809
auto specializedType = genericWitness.subst(substitutionMap, options);
826810
if (specializedType->hasError()) {
827-
if (isTentativeWitness())
811+
if (options.getTentativeTypeWitness)
828812
return { Type(), nullptr };
829813

830814
specializedType = ErrorType::get(genericWitness);
831815
}
832816

833-
// If we aren't in a case where we used the tentative type witness
834-
// information, cache the result.
817+
// Cache the result.
835818
auto specializedWitnessAndDecl = TypeWitnessAndDecl{specializedType, typeDecl};
836-
if (!isTentativeWitness() && !specializedType->hasError())
819+
if (!options.getTentativeTypeWitness && !specializedType->hasError())
837820
TypeWitnesses[assocType] = specializedWitnessAndDecl;
838821

839822
return specializedWitnessAndDecl;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
struct Row {}
4+
5+
// Force inference of Element first
6+
typealias Foo<T: Sequence> = T.Element
7+
typealias Bar = Foo<Row>
8+
9+
extension Row: Collection {
10+
public var startIndex: Int { 0 }
11+
12+
public var endIndex: Int { 0 }
13+
14+
public func index(after: Int) -> Int {
15+
fatalError()
16+
}
17+
18+
// We should not be considering this as a candidate witness
19+
public subscript<T>(_: T) -> Any {
20+
fatalError()
21+
}
22+
23+
// This is the one we want
24+
public subscript(position: Int) -> String {
25+
fatalError()
26+
}
27+
}
28+
29+
// This should type check because Row.Element == String, and not Any
30+
let x: Row.Element.Type = Row.Element.self
31+
let y: String.Type = x

0 commit comments

Comments
 (0)