Skip to content

Commit b96139e

Browse files
committed
[CSOptimizer] Emulate old hack for unary argument matching more precisely
Having it be part of the other matching wasn't a good idea because previous "favoring" happened only in a few situations - if argument was a declaration reference, application or (dynamic) subscript that had overload choice selected during constraint generation.
1 parent 26b86c2 commit b96139e

File tree

2 files changed

+75
-21
lines changed

2 files changed

+75
-21
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,52 @@ static void findFavoredChoicesBasedOnArity(
338338
favoredChoice(choice);
339339
}
340340

341+
/// Preserves old behavior where, for unary calls, the solver would not previously
342+
/// consider choices that didn't match on the number of parameters (regardless of
343+
/// defaults and variadics) and only exact matches were favored.
344+
static std::optional<DisjunctionInfo> preserveFavoringOfUnlabeledUnaryArgument(
345+
ConstraintSystem &cs, Constraint *disjunction, ArgumentList *argumentList) {
346+
if (!argumentList->isUnlabeledUnary())
347+
return std::nullopt;
348+
349+
auto ODRE = isOverloadedDeclRef(disjunction);
350+
bool preserveFavoringOfUnlabeledUnaryArgument =
351+
!ODRE || numOverloadChoicesMatchingOnArity(ODRE, argumentList) < 2;
352+
353+
if (!preserveFavoringOfUnlabeledUnaryArgument)
354+
return std::nullopt;
355+
356+
auto *argument =
357+
argumentList->getUnlabeledUnaryExpr()->getSemanticsProvidingExpr();
358+
// The hack operated on "favored" types and only declaration references,
359+
// applications, and (dynamic) subscripts had them if they managed to
360+
// get an overload choice selected during constraint generation.
361+
if (!(isExpr<DeclRefExpr>(argument) || isExpr<ApplyExpr>(argument) ||
362+
isExpr<SubscriptExpr>(argument) ||
363+
isExpr<DynamicSubscriptExpr>(argument)))
364+
return {/*score=*/0};
365+
366+
auto argumentType = cs.getType(argument);
367+
if (argumentType->hasTypeVariable() || argumentType->hasDependentMember())
368+
return {/*score=*/0};
369+
370+
SmallVector<Constraint *, 2> favoredChoices;
371+
forEachDisjunctionChoice(
372+
cs, disjunction,
373+
[&argumentType, &favoredChoices](Constraint *choice, ValueDecl *decl,
374+
FunctionType *overloadType) {
375+
if (overloadType->getNumParams() != 1)
376+
return;
377+
378+
auto paramType = overloadType->getParams()[0].getPlainType();
379+
if (paramType->isEqual(argumentType))
380+
favoredChoices.push_back(choice);
381+
});
382+
383+
return DisjunctionInfo(/*score=*/favoredChoices.empty() ? 0 : 1,
384+
favoredChoices);
385+
}
386+
341387
} // end anonymous namespace
342388

343389
/// Given a set of disjunctions, attempt to determine
@@ -444,6 +490,16 @@ static void determineBestChoicesInContext(
444490
}
445491
}
446492

493+
// Preserves old behavior where, for unary calls, the solver
494+
// would not consider choices that didn't match on the number
495+
// of parameters (regardless of defaults) and only exact
496+
// matches were favored.
497+
if (auto info = preserveFavoringOfUnlabeledUnaryArgument(cs, disjunction,
498+
argumentList)) {
499+
recordResult(disjunction, std::move(info.value()));
500+
continue;
501+
}
502+
447503
if (!isSupportedDisjunction(disjunction))
448504
continue;
449505

@@ -931,17 +987,6 @@ static void determineBestChoicesInContext(
931987
double bestScore = 0.0;
932988
SmallVector<std::pair<Constraint *, double>, 2> favoredChoices;
933989

934-
// Preserves old behavior where, for unary calls, the solver
935-
// would not consider choices that didn't match on the number
936-
// of parameters (regardless of defaults) and only exact
937-
// matches were favored.
938-
bool preserveFavoringOfUnlabeledUnaryArgument = false;
939-
if (argumentList->isUnlabeledUnary()) {
940-
auto ODRE = isOverloadedDeclRef(disjunction);
941-
preserveFavoringOfUnlabeledUnaryArgument =
942-
!ODRE || numOverloadChoicesMatchingOnArity(ODRE, argumentList) < 2;
943-
}
944-
945990
forEachDisjunctionChoice(
946991
cs, disjunction,
947992
[&](Constraint *choice, ValueDecl *decl, FunctionType *overloadType) {
@@ -970,15 +1015,6 @@ static void determineBestChoicesInContext(
9701015
onlyLiteralCandidates &&
9711016
(!canUseContextualResultTypes() || resultTypes.empty());
9721017

973-
if (preserveFavoringOfUnlabeledUnaryArgument) {
974-
// Old behavior completely disregarded the fact that some of
975-
// the parameters could be defaulted.
976-
if (overloadType->getNumParams() != 1)
977-
return;
978-
979-
favorExactMatchesOnly = true;
980-
}
981-
9821018
// This is important for SIMD operators in particular because
9831019
// a lot of their overloads have same-type requires to a concrete
9841020
// type: `<Scalar == (U)Int*>(_: SIMD*<Scalar>, ...) -> ...`.

test/Constraints/old_hack_related_ambiguities.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ func entity(_: Int) -> Int {
66

77
struct Test {
88
func test(_ v: Int) -> Int { v }
9+
// expected-note@-1 {{found this candidate}}
910
func test(_ v: Int?) -> Int? { v }
11+
// expected-note@-1 {{found this candidate}}
1012
}
1113

1214
func test_ternary_literal(v: Test) -> Int? {
13-
true ? v.test(0) : nil // Ok
15+
// Literals don't have a favored type
16+
true ? v.test(0) : nil // expected-error {{ambiguous use of 'test'}}
1417
}
1518

1619
func test_ternary(v: Test) -> Int? {
20+
// Because calls had favored types set if they were resolved during constraint generation.
1721
true ? v.test(entity(0)) : nil // Ok
1822
}
1923

@@ -159,12 +163,14 @@ do {
159163
var p: UnsafeMutableRawPointer { get { fatalError() } }
160164

161165
func f(_ p: UnsafeMutableRawPointer) {
166+
// The old hack (which is now removed) couldn't handle member references, only direct declaration references.
162167
guard let x = UnsafeMutablePointer<Double>(OpaquePointer(self.p)) else {
163168
return
164169
}
165170
_ = x
166171

167172
guard let x = UnsafeMutablePointer<Double>(OpaquePointer(p)) else {
173+
// expected-error@-1 {{initializer for conditional binding must have Optional type, not 'UnsafeMutablePointer<Double>'}}
168174
return
169175
}
170176
_ = x
@@ -257,3 +263,15 @@ func test_non_default_literal_use(arg: Float) {
257263
let v = arg * 2.0 // shouldn't use `(Float, Double) -> Double` overload
258264
let _: Float = v // Ok
259265
}
266+
267+
// This should be ambiguous without contextual type but was accepted before during to
268+
// unlabeled unary argument favoring.
269+
func test_variadic_static_member_is_preferred_over_partially_applied_instance_overload() {
270+
struct Test {
271+
func fn() {}
272+
static func fn(_: Test...) {}
273+
}
274+
275+
let t: Test
276+
Test.fn(t) // Ok
277+
}

0 commit comments

Comments
 (0)