Skip to content

Commit eca9200

Browse files
authored
Merge pull request #17855 from rudkx/rdar19748710
[ConstraintSystem] Fix the ordering of functions with optional parame…
2 parents ec66f1c + d271f4b commit eca9200

File tree

7 files changed

+116
-54
lines changed

7 files changed

+116
-54
lines changed

lib/Sema/CSRanking.cpp

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) {
6767
case SK_CollectionUpcastConversion:
6868
log << "collection upcast conversion";
6969
break;
70-
70+
71+
case SK_BindOptionalToArchetype:
72+
log << "bind optional to archetype";
73+
break;
74+
7175
case SK_ValueToOptional:
7276
log << "value to optional";
7377
break;
@@ -634,6 +638,28 @@ static bool isDeclAsSpecializedAs(TypeChecker &tc, DeclContext *dc,
634638
Type paramType1 = getAdjustedParamType(param1);
635639
Type paramType2 = getAdjustedParamType(param2);
636640

641+
// If we have:
642+
// param1Type = $T0?
643+
// param2Type = $T1
644+
// the subtype constraint check will always return true
645+
// since we'll attempt to bind $T1 as $T0?.
646+
//
647+
// What we're comparing here is foo<T>(_: T?) vs. foo<T>(_: T) and
648+
// we don't want to consider the optional-taking function to be the
649+
// the more specialized one since throughout the type system we
650+
// consider T to be a subtype of T?.
651+
SmallVector<Type, 2> optionals1;
652+
paramType1->lookThroughAllOptionalTypes(optionals1);
653+
auto numOptionals1 = optionals1.size();
654+
655+
SmallVector<Type, 2> optionals2;
656+
Type objType2 = paramType2->lookThroughAllOptionalTypes(optionals2);
657+
auto numOptionals2 = optionals2.size();
658+
659+
if (numOptionals1 > numOptionals2 &&
660+
(objType2->is<TypeVariableType>() || objType2->isAny()))
661+
return false;
662+
637663
// Check whether the first parameter is a subtype of the second.
638664
cs.addConstraint(ConstraintKind::Subtype,
639665
paramType1, paramType2, locator);
@@ -682,10 +708,7 @@ static bool isDeclAsSpecializedAs(TypeChecker &tc, DeclContext *dc,
682708

683709
if (!knownNonSubtype) {
684710
// Solve the system.
685-
auto solution = cs.solveSingle(FreeTypeVariableBinding::Allow);
686-
687-
// Ban value-to-optional conversions.
688-
if (solution && solution->getFixedScore().Data[SK_ValueToOptional] == 0)
711+
if (cs.solveSingle(FreeTypeVariableBinding::Allow))
689712
return true;
690713
}
691714

@@ -752,9 +775,6 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
752775
auto foundRefinement1 = false;
753776
auto foundRefinement2 = false;
754777

755-
bool isStdlibOptionalMPlusOperator1 = false;
756-
bool isStdlibOptionalMPlusOperator2 = false;
757-
758778
auto getWeight = [&](ConstraintLocator *locator) -> unsigned {
759779
if (auto *anchor = locator->getAnchor()) {
760780
auto weight = weights.find(anchor);
@@ -960,40 +980,6 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
960980
foundRefinement2 = decl2InSubprotocol;
961981
}
962982
}
963-
964-
// FIXME: Lousy hack for ?? to prefer the catamorphism (flattening)
965-
// over the mplus (non-flattening) overload if all else is equal.
966-
if (decl1->getBaseName() == "??") {
967-
assert(decl2->getBaseName() == "??");
968-
969-
auto check = [](const ValueDecl *VD) -> bool {
970-
if (!VD->getModuleContext()->isStdlibModule())
971-
return false;
972-
auto fnTy = VD->getInterfaceType()->castTo<AnyFunctionType>();
973-
if (!fnTy->getResult()->getOptionalObjectType())
974-
return false;
975-
976-
// Check that the standard library hasn't added another overload of
977-
// the ?? operator.
978-
auto params = fnTy->getParams();
979-
assert(params.size() == 2);
980-
981-
auto param1 = params[0].getType();
982-
auto param2 = params[1].getType()->castTo<AnyFunctionType>();
983-
984-
assert(param1->getOptionalObjectType());
985-
assert(param2->isAutoClosure());
986-
assert(param2->getResult()->getOptionalObjectType());
987-
988-
(void) param1;
989-
(void) param2;
990-
991-
return true;
992-
};
993-
994-
isStdlibOptionalMPlusOperator1 = check(decl1);
995-
isStdlibOptionalMPlusOperator2 = check(decl2);
996-
}
997983
}
998984

999985
// Compare the type variable bindings.
@@ -1109,14 +1095,6 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
11091095
}
11101096
}
11111097

1112-
// FIXME: All other things being equal, prefer the catamorphism (flattening)
1113-
// overload of ?? over the mplus (non-flattening) overload.
1114-
if (score1 == score2) {
1115-
// This is correct: we want to /disprefer/ the mplus.
1116-
score2 += isStdlibOptionalMPlusOperator1;
1117-
score1 += isStdlibOptionalMPlusOperator2;
1118-
}
1119-
11201098
// FIXME: There are type variables and overloads not common to both solutions
11211099
// that haven't been considered. They make the systems different, but don't
11221100
// affect ranking. We need to handle this.

lib/Sema/CSSimplify.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,9 @@ matchCallArguments(ConstraintSystem &cs, ConstraintKind kind,
683683
}
684684
}
685685

686+
if (!argType->isAny())
687+
cs.increaseScore(ScoreKind::SK_EmptyExistentialConversion);
688+
686689
return cs.getTypeMatchSuccess();
687690
}
688691

@@ -1657,6 +1660,16 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
16571660
auto *typeVar = typeVar1 ? typeVar1 : typeVar2;
16581661
auto type = typeVar1 ? type2 : type1;
16591662

1663+
if (type->getOptionalObjectType()) {
1664+
if (auto *typeVarLocator = typeVar->getImpl().getLocator()) {
1665+
auto path = typeVarLocator->getPath();
1666+
if (!path.empty() &&
1667+
path.back().getKind() == ConstraintLocator::Archetype) {
1668+
increaseScore(SK_BindOptionalToArchetype);
1669+
}
1670+
}
1671+
}
1672+
16601673
return matchTypesBindTypeVar(typeVar, type, kind, flags, locator,
16611674
formUnsolvedResult);
16621675
}

lib/Sema/ConstraintSystem.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ enum ScoreKind {
456456
SK_CollectionUpcastConversion,
457457
/// A value-to-optional conversion.
458458
SK_ValueToOptional,
459+
/// Instantiating a function with archetype T as an Optional value.
460+
SK_BindOptionalToArchetype,
459461
/// A conversion to an empty existential type ('Any' or '{}').
460462
SK_EmptyExistentialConversion,
461463
/// A key path application subscript.

test/Constraints/diag_ambiguities.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,19 @@ C(g) // expected-error{{ambiguous use of 'g'}}
2929
func h<T>(_ x: T) -> () {}
3030
C(h) // expected-error{{ambiguous use of 'init'}}
3131

32-
func rdar29691909_callee(_ o: AnyObject?) -> Any? { return o } // expected-note {{found this candidate}}
33-
func rdar29691909_callee(_ o: AnyObject) -> Any { return o } // expected-note {{found this candidate}}
32+
func rdar29691909_callee(_ o: AnyObject?) -> Any? { return o }
33+
func rdar29691909_callee(_ o: AnyObject) -> Any { return o }
3434

3535
func rdar29691909(o: AnyObject) -> Any? {
36-
return rdar29691909_callee(o) // expected-error{{ambiguous use of 'rdar29691909_callee'}}
36+
return rdar29691909_callee(o)
37+
}
38+
39+
func rdar29691909_callee_alt(_ o: AnyObject?) -> Int? { fatalError() }
40+
func rdar29691909_callee_alt(_ o: AnyObject) -> Int { fatalError() }
41+
42+
func rdar29691909_1(o: AnyObject) -> Int {
43+
let r = rdar29691909_callee_alt(o)
44+
return r
3745
}
3846

3947
func rdar29907555(_ value: Any!) -> String {

test/Constraints/overload.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,53 @@ func autoclosure1<T>(_: [T], _: X) { }
236236
func test_autoclosure1(ia: [Int]) {
237237
autoclosure1(ia, X()) // okay: resolves to the second function
238238
}
239+
240+
// Ensure that we select the (T?, T) -> T overload of '??' in coalesce() below.
241+
func apply<T>(_ fn: () -> T) -> T { return fn() }
242+
func opt(x: String) -> String { return x }
243+
func opt() -> String? { return "hi" }
244+
245+
func coalesce() {
246+
let _ = apply{
247+
_ = opt()
248+
return opt()
249+
} ?? ""
250+
}
251+
252+
// Ensure that we do not select the (T?, T) -> T version of ??, which
253+
// would result in a warning about RHS never being selected.
254+
func rdar19748710_0(_ value: Int?) -> Int? {
255+
return value ?? value
256+
}
257+
258+
// Again, ensure we do not select (T?, T) -> T, despite forcing the result.
259+
func rdar19748710_1(_ value: Int?) -> Int? {
260+
return (value ?? value)!
261+
}
262+
263+
// FIXME: We choose to inject the LHS, which really doesn't seem like
264+
// the reasonable choice here. It seems more predictable and safe to
265+
// either inject the result of ??, or treat this as an error since the
266+
// current behavior will result in .some(nil) being force-unwrapped
267+
// rather than the RHS value. The diagnostic is problematic, too,
268+
// since it claims 'Int?' is a non-optional type.
269+
func rdar19748710_2(_ value: Int?) -> Int? {
270+
return (value ?? value)!! // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'Int?', so the right side is never used}}
271+
}
272+
273+
// Ensure that we select the more closely matching function between
274+
// the two _g overloads.
275+
func giveAny() -> Any { fatalError() }
276+
func giveAnyOptional() -> Any? { fatalError() }
277+
func takeAny(_ a: Any?) -> Any? { fatalError() }
278+
func takeAny(_ a: Any) -> Any { fatalError() }
279+
280+
func testAnyOptional() {
281+
let r = takeAny(giveAnyOptional())
282+
let _ = r!
283+
}
284+
285+
func testAny() {
286+
let r = takeAny(giveAny())
287+
let _ = r! // expected-error {{cannot force unwrap value of non-optional type 'Any'}}
288+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
3+
// CHECK: sil hidden @$S15overload_silgen8anyToIntySiypF
4+
// CHECK: end sil function '$S15overload_silgen8anyToIntySiypF'
5+
func anyToInt(_: Any) -> Int { fatalError() }
6+
7+
// CHECK: sil hidden @$S15overload_silgen8anyToIntySiSgypSgF
8+
// CHECK: function_ref @$S15overload_silgen8anyToIntySiypF
9+
// CHECK: end sil function '$S15overload_silgen8anyToIntySiSgypSgF'
10+
func anyToInt(_ value: Any?) -> Int? { return anyToInt(value!) }

validation-test/Sema/OverridesAndOverloads.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ Overloads.test("generic methods are worse than non-generic") {
328328
}
329329

330330
Base().foo(C1()); expectEqual("foo(C1)", which)
331-
Base().foo(Token1()); expectEqual("foo(Any)", which)
331+
Base().foo(Token1()); expectEqual("foo(T)", which)
332+
Base().foo(Token1() as Any); expectEqual("foo(Any)", which)
332333

333334
Base().bar(C1()); expectEqual("bar(C1)", which)
334335
Base().bar(Token1()); expectEqual("bar(T)", which)

0 commit comments

Comments
 (0)