Skip to content

Commit 20019be

Browse files
committed
[CSSolver] Use correct locator when matching function result types related to closures
Currently we always use 'FunctionResult' as a path element when matching function result types, but closure result type is allowed to be implicitly converted to Void, which means we need to be careful when to use 'FunctionResult' and 'ClosureResult'. Resolves: rdar://problem/37790062
1 parent b124a51 commit 20019be

File tree

5 files changed

+111
-11
lines changed

5 files changed

+111
-11
lines changed

lib/Sema/CSRanking.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,14 +1007,32 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
10071007
// Compare the type variable bindings.
10081008
auto &tc = cs.getTypeChecker();
10091009
for (auto &binding : diff.typeBindings) {
1010+
auto type1 = binding.bindings[idx1];
1011+
auto type2 = binding.bindings[idx2];
1012+
1013+
auto &impl = binding.typeVar->getImpl();
1014+
1015+
if (auto *locator = impl.getLocator()) {
1016+
auto path = locator->getPath();
1017+
if (!path.empty() &&
1018+
path.back().getKind() == ConstraintLocator::ClosureResult) {
1019+
// Since we support `() -> T` to `() -> Void` and
1020+
// `() -> Never` to `() -> T` conversions, it's always
1021+
// preferable to pick `T` rather than `Never` with
1022+
// all else being equal.
1023+
if (type2->isUninhabited())
1024+
++score1;
1025+
1026+
if (type1->isUninhabited())
1027+
++score2;
1028+
}
1029+
}
1030+
10101031
// If the type variable isn't one for which we should be looking at the
10111032
// bindings, don't.
1012-
if (!binding.typeVar->getImpl().prefersSubtypeBinding())
1033+
if (!impl.prefersSubtypeBinding())
10131034
continue;
10141035

1015-
auto type1 = binding.bindings[idx1];
1016-
auto type2 = binding.bindings[idx2];
1017-
10181036
// If the types are equivalent, there's nothing more to do.
10191037
if (type1->isEqual(type2))
10201038
continue;

lib/Sema/CSSimplify.cpp

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,10 +1212,8 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
12121212
return result;
12131213

12141214
// Result type can be covariant (or equal).
1215-
return matchTypes(func1->getResult(), func2->getResult(), subKind,
1216-
subflags,
1217-
locator.withPathElement(
1218-
ConstraintLocator::FunctionResult));
1215+
return matchTypes(func1->getResult(), func2->getResult(), subKind, subflags,
1216+
locator.withPathElement(ConstraintLocator::FunctionResult));
12191217
}
12201218

12211219
ConstraintSystem::TypeMatchResult
@@ -1501,6 +1499,8 @@ ConstraintSystem::TypeMatchResult
15011499
ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
15021500
TypeMatchOptions flags,
15031501
ConstraintLocatorBuilder locator) {
1502+
auto origType1 = type1;
1503+
15041504
bool isArgumentTupleConversion
15051505
= kind == ConstraintKind::ArgumentTupleConversion ||
15061506
kind == ConstraintKind::OperatorArgumentTupleConversion;
@@ -2243,7 +2243,32 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
22432243
// Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure
22442244
// literals.
22452245
if (auto elt = locator.last()) {
2246-
if (elt->getKind() == ConstraintLocator::ClosureResult) {
2246+
auto isClosureResult = [&]() {
2247+
if (elt->getKind() == ConstraintLocator::ClosureResult)
2248+
return true;
2249+
2250+
// If constraint is matching function results where
2251+
// left-hand side is a 'closure result' we need to allow
2252+
// certain implicit conversions.
2253+
if (elt->getKind() != ConstraintLocator::FunctionResult)
2254+
return false;
2255+
2256+
if (auto *typeVar = origType1->getAs<TypeVariableType>()) {
2257+
auto *locator = typeVar->getImpl().getLocator();
2258+
if (!locator)
2259+
return false;
2260+
2261+
auto path = locator->getPath();
2262+
if (path.empty())
2263+
return false;
2264+
2265+
return path.back().getKind() == ConstraintLocator::ClosureResult;
2266+
}
2267+
2268+
return false;
2269+
};
2270+
2271+
if (isClosureResult()) {
22472272
if (concrete && kind >= ConstraintKind::Subtype &&
22482273
(type1->isUninhabited() || type2->isVoid())) {
22492274
increaseScore(SK_FunctionConversion);

test/Constraints/closures.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,6 @@ func rdar33429010_2() {
630630
let iter = I_33429010()
631631
var acc: Int = 0 // expected-warning {{}}
632632
let _: Int = AnySequence { iter }.rdar33429010(into: acc, { $0 + $1 })
633-
// expected-warning@-1 {{result of operator '+' is unused}}
634633
let _: Int = AnySequence { iter }.rdar33429010(into: acc, { $0.rdar33429010_incr($1) })
635634
}
636635

@@ -652,3 +651,16 @@ func rdar36054961() {
652651
str.replaceSubrange(range, with: str[range].reversed())
653652
}])
654653
}
654+
655+
func rdar37790062() {
656+
struct S<T> {
657+
init(_ a: () -> T, _ b: () -> T) {}
658+
}
659+
660+
func foo() -> Int { return 42 }
661+
func bar() -> Void {}
662+
func baz() -> (String, Int) { return ("question", 42) }
663+
664+
_ = S({ foo() }, { bar() }) // Ok, should infer T to be 'Void'
665+
_ = S({ baz() }, { bar() }) // Ok, should infer T to be 'Void'
666+
}

test/Constraints/rdar37790062.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol A {
4+
associatedtype V
5+
associatedtype E: Error
6+
7+
init(value: V)
8+
init(error: E)
9+
10+
func foo<U>(value: (V) -> U, error: (E) -> U) -> U
11+
}
12+
13+
enum R<T, E: Error> : A {
14+
case foo(T)
15+
case bar(E)
16+
17+
init(value: T) { self = .foo(value) }
18+
init(error: E) { self = .bar(error) }
19+
20+
func foo<R>(value: (T) -> R, error: (E) -> R) -> R {
21+
fatalError()
22+
}
23+
}
24+
25+
protocol P {
26+
associatedtype V
27+
28+
@discardableResult
29+
func baz(callback: @escaping (V) -> Void) -> Self
30+
}
31+
32+
class C<V> : P {
33+
func baz(callback: @escaping (V) -> Void) -> Self { return self }
34+
}
35+
class D<T, E: Error> : C<R<T, E>> {
36+
init(fn: (_ ret: @escaping (V) -> Void) -> Void) {}
37+
}
38+
39+
extension A where V: P, V.V: A, E == V.V.E {
40+
func bar() -> D<V.V.V, V.V.E> {
41+
return D { complete in
42+
foo(value: { promise in promise.baz { result in } },
43+
error: { complete(R(error: $0)) })
44+
}
45+
}
46+
}

test/expr/closure/closures.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,6 @@ func rdar19179412() -> (Int) -> Int {
259259
func takesVoidFunc(_ f: () -> ()) {}
260260
var i: Int = 1
261261

262-
// expected-warning @+1 {{expression of type 'Int' is unused}}
263262
takesVoidFunc({i})
264263
// expected-warning @+1 {{expression of type 'Int' is unused}}
265264
var f1: () -> () = {i}

0 commit comments

Comments
 (0)