Skip to content

Commit 527de22

Browse files
committed
[CSOptimizer] Emulate old behavior related to favoring of unary calls to members
Preserves old behavior where for unary calls to members the solver would not consider choices that didn't match on the number of parameters (regardless of defaults) and only exact matches were favored.
1 parent 670127a commit 527de22

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ void forEachDisjunctionChoice(
9191
}
9292
}
9393

94+
static bool isOverloadedDeclRef(Constraint *disjunction) {
95+
assert(disjunction->getKind() == ConstraintKind::Disjunction);
96+
return disjunction->getLocator()->directlyAt<OverloadedDeclRefExpr>();
97+
}
98+
9499
} // end anonymous namespace
95100

96101
/// Given a set of disjunctions, attempt to determine
@@ -232,6 +237,7 @@ static void determineBestChoicesInContext(
232237
enum class MatchFlag {
233238
OnParam = 0x01,
234239
Literal = 0x02,
240+
ExactOnly = 0x04,
235241
};
236242

237243
using MatchOptions = OptionSet<MatchFlag>;
@@ -250,6 +256,9 @@ static void determineBestChoicesInContext(
250256
scoreCandidateMatch = [&](GenericSignature genericSig,
251257
Type candidateType, Type paramType,
252258
MatchOptions options) -> double {
259+
if (options.contains(MatchFlag::ExactOnly))
260+
return candidateType->isEqual(paramType) ? 1 : 0;
261+
253262
// Exact match between candidate and parameter types.
254263
if (candidateType->isEqual(paramType))
255264
return options.contains(MatchFlag::Literal) ? 0.3 : 1;
@@ -267,17 +276,17 @@ static void determineBestChoicesInContext(
267276
paramType = paramType->lookThroughAllOptionalTypes(paramOptionals);
268277

269278
if (!candidateOptionals.empty() || !paramOptionals.empty()) {
270-
if (paramOptionals.size() >= candidateOptionals.size())
279+
if (paramOptionals.size() >= candidateOptionals.size()) {
271280
return scoreCandidateMatch(genericSig, candidateType, paramType,
272281
options);
282+
}
273283

274284
// Optionality mismatch.
275285
return 0;
276286
}
277287
}
278288

279-
// Candidate could be injected into optional parameter type
280-
// or converted to a superclass.
289+
// Candidate could be converted to a superclass.
281290
if (isSubclassOf(candidateType, paramType))
282291
return 1;
283292

@@ -343,6 +352,8 @@ static void determineBestChoicesInContext(
343352
double bestScore = 0.0;
344353
SmallVector<std::pair<Constraint *, double>, 2> favoredChoices;
345354

355+
bool isOverloadedDeclRefDisjunction = isOverloadedDeclRef(disjunction);
356+
346357
forEachDisjunctionChoice(
347358
cs, disjunction,
348359
[&](Constraint *choice, ValueDecl *decl, FunctionType *overloadType) {
@@ -367,6 +378,20 @@ static void determineBestChoicesInContext(
367378
if (!matchings)
368379
return;
369380

381+
bool favorExactMatchesOnly = false;
382+
// Preserves old behavior where for unary calls to members
383+
// the solver would not consider choices that didn't match on
384+
// the number of parameters (regardless of defaults) and only
385+
// exact matches were favored.
386+
if (!isOverloadedDeclRefDisjunction && argumentList->size() == 1) {
387+
// Old behavior completely disregarded the fact that some of
388+
// the parameters could be defaulted.
389+
if (overloadType->getNumParams() != 1)
390+
return;
391+
392+
favorExactMatchesOnly = true;
393+
}
394+
370395
double score = 0.0;
371396
for (unsigned paramIdx = 0, n = overloadType->getNumParams();
372397
paramIdx != n; ++paramIdx) {
@@ -444,6 +469,8 @@ static void determineBestChoicesInContext(
444469
MatchOptions options(MatchFlag::OnParam);
445470
if (isLiteralDefault)
446471
options |= MatchFlag::Literal;
472+
if (favorExactMatchesOnly)
473+
options |= MatchFlag::ExactOnly;
447474

448475
auto score = scoreCandidateMatch(genericSig, candidateType,
449476
paramType, options);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
func entity(_: Int) -> Int {
4+
0
5+
}
6+
7+
struct Test {
8+
func test(_ v: Int) -> Int { v }
9+
func test(_ v: Int?) -> Int? { v }
10+
}
11+
12+
func test_ternary_literal(v: Test) -> Int? {
13+
true ? v.test(0) : nil // Ok
14+
}
15+
16+
func test_ternary(v: Test) -> Int? {
17+
true ? v.test(entity(0)) : nil // Ok
18+
}
19+
20+
do {
21+
struct TestFloat {
22+
func test(_ v: Float) -> Float { v } // expected-note {{found this candidate}}
23+
func test(_ v: Float?) -> Float? { v } // expected-note {{found this candidate}}
24+
}
25+
26+
func test_ternary_non_default_literal(v: TestFloat) -> Float? {
27+
true ? v.test(1.0) : nil // expected-error {{ambiguous use of 'test'}}
28+
}
29+
}
30+
31+
do {
32+
struct Test {
33+
init(a: Int, b: Int = 0) throws {}
34+
init?(a: Int?) {}
35+
}
36+
37+
func test(v: Int) -> Test? {
38+
return Test(a: v) // Ok
39+
}
40+
}
41+

0 commit comments

Comments
 (0)