Skip to content

Commit c81f84e

Browse files
committed
AST: Fix over-eager collapseSpecializedConformance()
It's possible that the conforming type is equal to the generic conformance type, but some of the substitutions replace an abstract conformance with a concrete one. In this case we cannot collapse away the specialized conformance, because we lose information that way. Fixes <rdar://problem/40164371>.
1 parent 63f5a8d commit c81f84e

File tree

3 files changed

+91
-10
lines changed

3 files changed

+91
-10
lines changed

include/swift/AST/SubstitutionMap.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ class SubstitutionMap {
181181
/// Profile the substitution map, for use with LLVM's FoldingSet.
182182
void profile(llvm::FoldingSetNodeID &id) const;
183183

184+
const llvm::DenseMap<CanType, SmallVector<ProtocolConformanceRef, 1>> &
185+
getConformanceMap() const { return conformanceMap; }
186+
184187
private:
185188
friend class GenericSignature;
186189
friend class GenericEnvironment;

lib/AST/ASTContext.cpp

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,24 +1816,57 @@ ASTContext::getConformance(Type conformingType,
18161816
/// that instead.
18171817
static ProtocolConformance *collapseSpecializedConformance(
18181818
Type type,
1819-
ProtocolConformance *conformance) {
1819+
ProtocolConformance *conformance,
1820+
SubstitutionList substitutions) {
18201821
while (true) {
1821-
// If the conformance matches, return it.
1822-
if (conformance->getType()->isEqual(type))
1823-
return conformance;
1824-
18251822
switch (conformance->getKind()) {
1826-
case ProtocolConformanceKind::Inherited:
1827-
conformance = cast<InheritedProtocolConformance>(conformance)
1828-
->getInheritedConformance();
1823+
case ProtocolConformanceKind::Specialized:
1824+
conformance = cast<SpecializedProtocolConformance>(conformance)
1825+
->getGenericConformance();
18291826
break;
18301827

1828+
case ProtocolConformanceKind::Normal:
1829+
case ProtocolConformanceKind::Inherited:
1830+
// If the conformance matches, return it.
1831+
if (conformance->getType()->isEqual(type)) {
1832+
for (auto substitution : substitutions)
1833+
for (auto subConformance : substitution.getConformances())
1834+
if (!subConformance.isAbstract())
1835+
return nullptr;
1836+
1837+
return conformance;
1838+
}
1839+
1840+
return nullptr;
1841+
}
1842+
}
1843+
}
1844+
1845+
/// If one of the ancestor conformances already has a matching type, use
1846+
/// that instead.
1847+
static ProtocolConformance *collapseSpecializedConformance(
1848+
Type type,
1849+
ProtocolConformance *conformance,
1850+
const SubstitutionMap &substitutions) {
1851+
while (true) {
1852+
switch (conformance->getKind()) {
18311853
case ProtocolConformanceKind::Specialized:
18321854
conformance = cast<SpecializedProtocolConformance>(conformance)
18331855
->getGenericConformance();
18341856
break;
18351857

18361858
case ProtocolConformanceKind::Normal:
1859+
case ProtocolConformanceKind::Inherited:
1860+
// If the conformance matches, return it.
1861+
if (conformance->getType()->isEqual(type)) {
1862+
for (auto conformances : substitutions.getConformanceMap())
1863+
for (auto subConformance : conformances.second)
1864+
if (!subConformance.isAbstract())
1865+
return nullptr;
1866+
1867+
return conformance;
1868+
}
1869+
18371870
return nullptr;
18381871
}
18391872
}
@@ -1848,7 +1881,8 @@ ASTContext::getSpecializedConformance(Type type,
18481881
// a prior conformance (e.g., mapping into and then out of a conformance),
18491882
// return the existing conformance.
18501883
if (!alreadyCheckedCollapsed) {
1851-
if (auto existing = collapseSpecializedConformance(type, generic)) {
1884+
if (auto existing = collapseSpecializedConformance(type, generic,
1885+
substitutions)) {
18521886
++NumCollapsedSpecializedProtocolConformances;
18531887
return existing;
18541888
}
@@ -1885,7 +1919,7 @@ ASTContext::getSpecializedConformance(Type type,
18851919
// If we are performing a substitution that would get us back to the
18861920
// a prior conformance (e.g., mapping into and then out of a conformance),
18871921
// return the existing conformance.
1888-
if (auto existing = collapseSpecializedConformance(type, generic)) {
1922+
if (auto existing = collapseSpecializedConformance(type, generic, subMap)) {
18891923
++NumCollapsedSpecializedProtocolConformances;
18901924
return existing;
18911925
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-swift-frontend -emit-sil %s
2+
3+
protocol X1 {
4+
associatedtype X3 : X4
5+
}
6+
7+
protocol X4 {
8+
associatedtype X15
9+
}
10+
11+
protocol X7 { }
12+
13+
protocol X9 : X7 {
14+
associatedtype X10 : X7
15+
}
16+
17+
struct X12 : X9 {
18+
typealias X10 = X12
19+
}
20+
21+
struct X13<I1 : X7> : X9 {
22+
typealias X10 = X13<I1>
23+
}
24+
25+
struct X14<G : X4> : X4 where G.X15 : X9 {
26+
typealias X15 = X13<G.X15.X10>
27+
}
28+
29+
struct X17<A : X4> : X1 where A.X15 == X12 {
30+
typealias X3 = X14<A>
31+
}
32+
33+
struct X18 : X4 {
34+
typealias X15 = X12
35+
}
36+
37+
@_transparent
38+
func callee<T>(_: T) where T : X1 {
39+
let _: T.X3.X15? = nil
40+
}
41+
42+
func caller(b: X17<X18>) {
43+
callee(b)
44+
}

0 commit comments

Comments
 (0)