Skip to content

Commit 124df8e

Browse files
authored
Merge pull request #71862 from slavapestov/fix-rdar123334433
Sema: Associated type inference optimization
2 parents 545a7ee + 14b110d commit 124df8e

File tree

2 files changed

+120
-1
lines changed

2 files changed

+120
-1
lines changed

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "swift/AST/Types.h"
4646
#include "swift/AST/TypeCheckRequests.h"
4747
#include "swift/Basic/Defer.h"
48+
#include "swift/Basic/Statistic.h"
4849
#include "swift/ClangImporter/ClangModule.h"
4950
#include "llvm/ADT/Statistic.h"
5051
#include "llvm/ADT/TinyPtrVector.h"
@@ -578,6 +579,24 @@ struct InferredAssociatedTypesByWitness {
578579

579580
void dump(llvm::raw_ostream &out, unsigned indent) const;
580581

582+
bool operator==(const InferredAssociatedTypesByWitness &other) const {
583+
if (Inferred.size() != other.Inferred.size())
584+
return false;
585+
586+
for (unsigned i = 0, e = Inferred.size(); i < e; ++i) {
587+
if (Inferred[i].first != other.Inferred[i].first)
588+
return false;
589+
if (!Inferred[i].second->isEqual(other.Inferred[i].second))
590+
return false;
591+
}
592+
593+
return true;
594+
}
595+
596+
bool operator!=(const InferredAssociatedTypesByWitness &other) const {
597+
return !(*this == other);
598+
}
599+
581600
SWIFT_DEBUG_DUMP;
582601
};
583602

@@ -1533,6 +1552,39 @@ static InferenceCandidateKind checkInferenceCandidate(
15331552
return InferenceCandidateKind::Good;
15341553
}
15351554

1555+
/// If all terms introduce identical bindings and none come from a protocol
1556+
/// extension, no choice between them can change the chosen solution, so
1557+
/// collapse down to one.
1558+
///
1559+
/// WARNING: This does not readily generalize to disjunctions that have
1560+
/// multiple duplicated terms, eg A \/ A \/ B \/ B, because the relative
1561+
/// order of the value witnesses binding each A and each B might be weird.
1562+
static void tryOptimizeDisjunction(InferredAssociatedTypesByWitnesses &result) {
1563+
// We assume there is at least one term.
1564+
if (result.empty())
1565+
return;
1566+
1567+
for (unsigned i = 0, e = result.size(); i < e; ++i) {
1568+
// Skip the optimization if we have non-viable bindings anywhere.
1569+
if (!result[i].NonViable.empty())
1570+
return;
1571+
1572+
// Skip the optimization if anything came from a default type alias
1573+
// or protocol extension; the ranking is hairier in that case.
1574+
if (!result[i].Witness ||
1575+
result[i].Witness->getDeclContext()->getExtendedProtocolDecl())
1576+
return;
1577+
1578+
// Skip the optimization if any two consecutive terms contain distinct
1579+
// bindings.
1580+
if (i > 0 && result[i - 1] != result[i])
1581+
return;
1582+
}
1583+
1584+
// This disjunction is trivial.
1585+
result.resize(1);
1586+
}
1587+
15361588
/// Create an initial constraint system for the associated type inference solver.
15371589
///
15381590
/// Each protocol requirement defines a disjunction, where each disjunction
@@ -1743,7 +1795,9 @@ AssociatedTypeInference::getPotentialTypeWitnessesFromRequirement(
17431795

17441796
result.push_back(std::move(witnessResult));
17451797
next_witness:;
1746-
}
1798+
}
1799+
1800+
tryOptimizeDisjunction(result);
17471801

17481802
if (hadTautologicalWitness && !result.empty()) {
17491803
// Create a dummy entry, but only if there was at least one other witness;
@@ -3380,6 +3434,9 @@ AssociatedTypeDecl *AssociatedTypeInference::inferAbstractTypeWitnesses(
33803434
void AssociatedTypeInference::findSolutions(
33813435
ArrayRef<AssociatedTypeDecl *> unresolvedAssocTypes,
33823436
SmallVectorImpl<InferredTypeWitnessesSolution> &solutions) {
3437+
FrontendStatsTracer StatsTracer(getASTContext().Stats,
3438+
"associated-type-inference", conformance);
3439+
33833440
SmallVector<InferredTypeWitnessesSolution, 4> nonViableSolutions;
33843441
SmallVector<std::pair<ValueDecl *, ValueDecl *>, 4> valueWitnesses;
33853442
findSolutionsRec(unresolvedAssocTypes, solutions, nonViableSolutions,
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol P1 {}
4+
protocol P2 {}
5+
protocol P3 {}
6+
protocol P4 {}
7+
protocol P5 {}
8+
protocol P6 {}
9+
protocol P7 {}
10+
protocol P8 {}
11+
protocol P9 {}
12+
protocol P10 {}
13+
protocol P11 {}
14+
protocol P12 {}
15+
protocol P13 {}
16+
protocol P14 {}
17+
protocol P15 {}
18+
protocol P16 {}
19+
20+
protocol P {
21+
associatedtype A
22+
associatedtype B
23+
24+
func f<T: P1>(_: T, _: A, _: B)
25+
func f<T: P2>(_: T, _: A, _: B)
26+
func f<T: P3>(_: T, _: A, _: B)
27+
func f<T: P4>(_: T, _: A, _: B)
28+
func f<T: P5>(_: T, _: A, _: B)
29+
func f<T: P6>(_: T, _: A, _: B)
30+
func f<T: P7>(_: T, _: A, _: B)
31+
func f<T: P8>(_: T, _: A, _: B)
32+
func f<T: P9>(_: T, _: A, _: B)
33+
func f<T: P10>(_: T, _: A, _: B)
34+
func f<T: P11>(_: T, _: A, _: B)
35+
func f<T: P12>(_: T, _: A, _: B)
36+
func f<T: P13>(_: T, _: A, _: B)
37+
func f<T: P14>(_: T, _: A, _: B)
38+
func f<T: P15>(_: T, _: A, _: B)
39+
func f<T: P16>(_: T, _: A, _: B)
40+
}
41+
42+
struct G<A>: P {
43+
func f<T: P1>(_: T, _: A, _: Bool) {}
44+
func f<T: P2>(_: T, _: A, _: Bool) {}
45+
func f<T: P3>(_: T, _: A, _: Bool) {}
46+
func f<T: P4>(_: T, _: A, _: Bool) {}
47+
func f<T: P5>(_: T, _: A, _: Bool) {}
48+
func f<T: P6>(_: T, _: A, _: Bool) {}
49+
func f<T: P7>(_: T, _: A, _: Bool) {}
50+
func f<T: P8>(_: T, _: A, _: Bool) {}
51+
func f<T: P9>(_: T, _: A, _: Bool) {}
52+
func f<T: P10>(_: T, _: A, _: Bool) {}
53+
func f<T: P11>(_: T, _: A, _: Bool) {}
54+
func f<T: P12>(_: T, _: A, _: Bool) {}
55+
func f<T: P13>(_: T, _: A, _: Bool) {}
56+
func f<T: P14>(_: T, _: A, _: Bool) {}
57+
func f<T: P15>(_: T, _: A, _: Bool) {}
58+
func f<T: P16>(_: T, _: A, _: Bool) {}
59+
}
60+
61+
let x: Int.Type = G<Int>.A.self
62+
let y: Bool.Type = G<Int>.B.self

0 commit comments

Comments
 (0)