Skip to content

Commit 496fdab

Browse files
committed
Restore a very narrow function argument conversion for -swift-version 4.
Allow functions with type `(()) -> T` to be passed in places where we expect `() -> T`, but only for -swift-version 4 (for -swift-version 3 this already works due to other horrible things in CSSimplify.cpp). We need to look at how we can help migrate these cases to -swift-version 5, but in the meantime, but that is something we can consider separately. Fixes rdar://problem/36670720 / https://bugs.swift.org/browse/SR-6796 (cherry picked from commit 6310aca)
1 parent 3f53f4f commit 496fdab

File tree

4 files changed

+72
-4
lines changed

4 files changed

+72
-4
lines changed

lib/Sema/CSApply.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6127,9 +6127,12 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
61276127
// func bar() -> ((()) -> Void)? { return nil }
61286128
// foo(bar) // This expression should compile in Swift 3 mode
61296129
// ```
6130-
if (tc.getLangOpts().isSwiftVersion3()) {
6131-
auto obj1 = fromType->getAnyOptionalObjectType();
6132-
auto obj2 = toType->getAnyOptionalObjectType();
6130+
//
6131+
// See also: https://bugs.swift.org/browse/SR-6796
6132+
if (cs.getASTContext().isSwiftVersionAtLeast(3) &&
6133+
!cs.getASTContext().isSwiftVersionAtLeast(5)) {
6134+
auto obj1 = fromType->getOptionalObjectType();
6135+
auto obj2 = toType->getOptionalObjectType();
61336136

61346137
if (obj1 && obj2) {
61356138
auto *fn1 = obj1->getAs<AnyFunctionType>();

lib/Sema/CSSimplify.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,35 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
11901190
}
11911191
}
11921192

1193+
// https://bugs.swift.org/browse/SR-6796
1194+
// Add a super-narrow hack to allow:
1195+
// (()) -> T to be passed in place of () -> T
1196+
if (getASTContext().isSwiftVersionAtLeast(4) &&
1197+
!getASTContext().isSwiftVersionAtLeast(5)) {
1198+
SmallVector<LocatorPathElt, 4> path;
1199+
locator.getLocatorParts(path);
1200+
1201+
// Find the last path element, skipping GenericArgument elements
1202+
// so that we allow this exception in cases of optional types, and
1203+
// skipping OptionalPayload elements so that we allow this
1204+
// exception in cases of optional injection.
1205+
auto last = std::find_if(
1206+
path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool {
1207+
return elt.getKind() != ConstraintLocator::GenericArgument &&
1208+
elt.getKind() != ConstraintLocator::OptionalPayload;
1209+
});
1210+
1211+
if (last != path.rend()) {
1212+
if (last->getKind() == ConstraintLocator::ApplyArgToParam) {
1213+
if (auto *paren1 = dyn_cast<ParenType>(func1Input.getPointer())) {
1214+
auto innerTy = paren1->getUnderlyingType();
1215+
if (func2Input->isVoid() && innerTy->isVoid())
1216+
func1Input = innerTy;
1217+
}
1218+
}
1219+
}
1220+
}
1221+
11931222
// Input types can be contravariant (or equal).
11941223
SmallVector<AnyFunctionType::Param, 4> func1Params;
11951224
SmallVector<AnyFunctionType::Param, 4> func2Params;

test/Compatibility/tuple_arguments.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,3 +1502,14 @@ do {
15021502
takeFn(fn: { (pair: (Int, Int?)) in } )
15031503
takeFn { (pair: (Int, Int?)) in }
15041504
}
1505+
1506+
// https://bugs.swift.org/browse/SR-6796
1507+
do {
1508+
func f(a: (() -> Void)? = nil) {}
1509+
func log<T>() -> ((T) -> Void)? { return nil }
1510+
1511+
f(a: log() as ((()) -> Void)?) // Allow ((()) -> Void)? to be passed in place of (() -> Void)?
1512+
1513+
func logNoOptional<T>() -> (T) -> Void { }
1514+
f(a: logNoOptional() as ((()) -> Void)) // Also allow the optional-injected form.
1515+
}

test/Constraints/tuple_arguments.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1626,7 +1626,21 @@ func rdar33239714() {
16261626
do {
16271627
func foo(_: (() -> Void)?) {}
16281628
func bar() -> ((()) -> Void)? { return nil }
1629-
foo(bar()) // expected-error {{cannot convert value of type '((()) -> Void)?' to expected argument type '(() -> Void)?'}}
1629+
foo(bar()) // Allow ((()) -> Void)? to be passed in place of (() -> Void)? for -swift-version 4 but not later.
1630+
}
1631+
1632+
// https://bugs.swift.org/browse/SR-6509
1633+
public extension Optional {
1634+
public func apply<Result>(_ transform: ((Wrapped) -> Result)?) -> Result? {
1635+
return self.flatMap { value in
1636+
transform.map { $0(value) }
1637+
}
1638+
}
1639+
1640+
public func apply<Value, Result>(_ value: Value?) -> Result?
1641+
where Wrapped == (Value) -> Result {
1642+
return value.apply(self)
1643+
}
16301644
}
16311645

16321646
// https://bugs.swift.org/browse/SR-6837
@@ -1639,3 +1653,14 @@ do {
16391653
takeFn { (pair: (Int, Int?)) in } // Disallow for -swift-version 4 and later
16401654
// expected-error@-1 {{contextual closure type '(Int, Int?) -> ()' expects 2 arguments, but 1 was used in closure body}}
16411655
}
1656+
1657+
// https://bugs.swift.org/browse/SR-6796
1658+
do {
1659+
func f(a: (() -> Void)? = nil) {}
1660+
func log<T>() -> ((T) -> Void)? { return nil }
1661+
1662+
f(a: log() as ((()) -> Void)?) // Allow ((()) -> Void)? to be passed in place of (() -> Void)? for -swift-version 4 but not later.
1663+
1664+
func logNoOptional<T>() -> (T) -> Void { }
1665+
f(a: logNoOptional() as ((()) -> Void)) // Also allow the optional-injected form.
1666+
}

0 commit comments

Comments
 (0)