Skip to content

Commit 3d32e89

Browse files
committed
[Constraint solver] Favor more-specialized overload among two generics.
When we form an overload set containing two generic functions, where one is more specialized than the other, "favor" the more-specialized function in the constraint system so we won't explore any paths involving the less-specialized function when the more-specialized version applies. This should be a strict improvement, because previously we would have always gone down both paths. Fixes rdar://problem/37371815, taking this exponential example linear.
1 parent 43435af commit 3d32e89

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,6 +1389,29 @@ void ConstraintSystem::addOverloadSet(Type boundType,
13891389
return;
13901390
}
13911391

1392+
// Performance hack: if there are two generic overloads, and one is
1393+
// more specialized than the other, prefer the more-specialized one.
1394+
if (!favoredChoice && choices.size() == 2 &&
1395+
choices[0].isDecl() && choices[1].isDecl() &&
1396+
isa<AbstractFunctionDecl>(choices[0].getDecl()) &&
1397+
cast<AbstractFunctionDecl>(choices[0].getDecl())->isGeneric() &&
1398+
isa<AbstractFunctionDecl>(choices[1].getDecl()) &&
1399+
cast<AbstractFunctionDecl>(choices[1].getDecl())->isGeneric()) {
1400+
switch (TC.compareDeclarations(DC, choices[0].getDecl(),
1401+
choices[1].getDecl())) {
1402+
case Comparison::Better:
1403+
favoredChoice = const_cast<OverloadChoice *>(&choices[0]);
1404+
break;
1405+
1406+
case Comparison::Worse:
1407+
favoredChoice = const_cast<OverloadChoice *>(&choices[1]);
1408+
break;
1409+
1410+
case Comparison::Unordered:
1411+
break;
1412+
}
1413+
}
1414+
13921415
SmallVector<Constraint *, 4> overloads;
13931416

13941417
// As we do for other favored constraints, if a favored overload has been
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %scale-test --begin 1 --end 10 --step 1 --select incrementScopeCounter %s --expected-exit-code 0
2+
// REQUIRES: OS=macosx
3+
// REQUIRES: asserts
4+
5+
protocol P { }
6+
7+
extension Int : P { }
8+
9+
struct Wrapper<T: P>: P { }
10+
11+
func foo<T>(_ value: T) -> T { return value }
12+
func foo<T: P>(_ value: T) -> Wrapper<T> { return Wrapper<T>() }
13+
14+
func acceptValues(_: Any...) { }
15+
func acceptValue(_: Any) { }
16+
17+
func test() {
18+
acceptValue(
19+
% for i in range(N):
20+
foo(
21+
% end
22+
0
23+
% for i in range(N):
24+
)
25+
% end
26+
)
27+
}

0 commit comments

Comments
 (0)