Skip to content

Commit 186c2f2

Browse files
committed
[ConstraintSystem] Fix the ordering of functions with optional parameters.
Treat non-optional generic parameters as being more specialized than optional generic parameters, and penalize any solutions that involve generic arguments that are themselves Optional. By doing these things, we can remove the special-cased code for the two overloads of '??' in the stdlib, instead treating the (T?, T) overload as better than the (T?, T?) overload except where a user actually passes an optionally-typed value as the second parameter. Fixes: rdar://problem/19748710
1 parent 9c99ee1 commit 186c2f2

File tree

5 files changed

+68
-53
lines changed

5 files changed

+68
-53
lines changed

lib/Sema/CSRanking.cpp

Lines changed: 27 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,27 @@ 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 && objType2->is<TypeVariableType>())
660+
return false;
661+
637662
// Check whether the first parameter is a subtype of the second.
638663
cs.addConstraint(ConstraintKind::Subtype,
639664
paramType1, paramType2, locator);
@@ -682,10 +707,7 @@ static bool isDeclAsSpecializedAs(TypeChecker &tc, DeclContext *dc,
682707

683708
if (!knownNonSubtype) {
684709
// 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)
710+
if (cs.solveSingle(FreeTypeVariableBinding::Allow))
689711
return true;
690712
}
691713

@@ -752,9 +774,6 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
752774
auto foundRefinement1 = false;
753775
auto foundRefinement2 = false;
754776

755-
bool isStdlibOptionalMPlusOperator1 = false;
756-
bool isStdlibOptionalMPlusOperator2 = false;
757-
758777
auto getWeight = [&](ConstraintLocator *locator) -> unsigned {
759778
if (auto *anchor = locator->getAnchor()) {
760779
auto weight = weights.find(anchor);
@@ -960,40 +979,6 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
960979
foundRefinement2 = decl2InSubprotocol;
961980
}
962981
}
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-
}
997982
}
998983

999984
// Compare the type variable bindings.
@@ -1109,14 +1094,6 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
11091094
}
11101095
}
11111096

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-
11201097
// FIXME: There are type variables and overloads not common to both solutions
11211098
// that haven't been considered. They make the systems different, but don't
11221099
// affect ranking. We need to handle this.

lib/Sema/CSSimplify.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,16 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
16571657
auto *typeVar = typeVar1 ? typeVar1 : typeVar2;
16581658
auto type = typeVar1 ? type2 : type1;
16591659

1660+
if (type->getOptionalObjectType()) {
1661+
if (auto *typeVarLocator = typeVar->getImpl().getLocator()) {
1662+
auto path = typeVarLocator->getPath();
1663+
if (!path.empty() &&
1664+
path.back().getKind() == ConstraintLocator::Archetype) {
1665+
increaseScore(SK_BindOptionalToArchetype);
1666+
}
1667+
}
1668+
}
1669+
16601670
return matchTypesBindTypeVar(typeVar, type, kind, flags, locator,
16611671
formUnsolvedResult);
16621672
}

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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,21 @@ 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(_ value: Int?) -> Int? {
255+
return value ?? value
256+
}

0 commit comments

Comments
 (0)