Skip to content

Commit 860cddf

Browse files
committed
[ConstraintSystem] Allow arguments to be passed by value to @autoclosure parameters
Instead of always requiring a call to be made to pass argument to `@autoclosure` parameter, it should be allowed to pass argument by value to `@autoclosure` parameter which can return a function type. ```swift func foo<T>(_ fn: @autoclosure () -> T) {} func bar(_ fn: @autoclosure @escaping () -> Int) { foo(fn) } ```
1 parent d417017 commit 860cddf

File tree

7 files changed

+124
-53
lines changed

7 files changed

+124
-53
lines changed

lib/Sema/CSApply.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5678,20 +5678,27 @@ Expr *ExprRewriter::coerceCallArguments(
56785678
continue;
56795679
}
56805680

5681-
auto isAutoClosureArg = [](Expr *arg) -> bool {
5682-
if (auto *DRE = dyn_cast<DeclRefExpr>(arg)) {
5683-
if (auto *PD = dyn_cast<ParamDecl>(DRE->getDecl()))
5684-
return PD->isAutoClosure();
5681+
Expr *convertedArg = nullptr;
5682+
auto argRequiresAutoClosureExpr = [&](const AnyFunctionType::Param &param,
5683+
Type argType) {
5684+
if (!param.isAutoClosure())
5685+
return false;
5686+
5687+
// Since it was allowed to pass function types to @autoclosure
5688+
// parameters in Swift versions < 5, it has to be handled as
5689+
// a regular function coversion by `coerceToType`.
5690+
if (isAutoClosureArgument(arg)) {
5691+
// In Swift >= 5 mode we only allow `@autoclosure` arguments
5692+
// to be used by value if parameter would return a function
5693+
// type (it just needs to get wrapped into autoclosure expr),
5694+
// otherwise argument must always form a call.
5695+
return cs.getASTContext().isSwiftVersionAtLeast(5);
56855696
}
5686-
return false;
5697+
5698+
return true;
56875699
};
56885700

5689-
Expr *convertedArg = nullptr;
5690-
// Since it was allowed to pass function types to @autoclosure
5691-
// parameters in Swift versions < 5, it has to be handled as
5692-
// a regular function coversion by `coerceToType`.
5693-
if (param.isAutoClosure() && (!argType->is<FunctionType>() ||
5694-
!isAutoClosureArg(arg))) {
5701+
if (argRequiresAutoClosureExpr(param, argType)) {
56955702
assert(!param.isInOut());
56965703

56975704
// If parameter is an autoclosure, we need to make sure that:

lib/Sema/CSDiagnostics.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,6 +1760,14 @@ bool MissingCallFailure::diagnoseAsError() {
17601760
return true;
17611761
}
17621762

1763+
case ConstraintLocator::FunctionResult: {
1764+
path = path.drop_back();
1765+
if (path.back().getKind() != ConstraintLocator::AutoclosureResult)
1766+
break;
1767+
1768+
LLVM_FALLTHROUGH;
1769+
}
1770+
17631771
case ConstraintLocator::AutoclosureResult: {
17641772
auto &cs = getConstraintSystem();
17651773
auto loc = cs.getConstraintLocator(getRawAnchor(), path.drop_back(),

lib/Sema/CSSimplify.cpp

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -978,30 +978,6 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
978978
? ConstraintKind::OperatorArgumentConversion
979979
: ConstraintKind::ArgumentConversion);
980980

981-
// Check whether argument of the call at given position refers to
982-
// parameter marked as `@autoclosure`. This function is used to
983-
// maintain source compatibility with Swift versions < 5,
984-
// previously examples like following used to type-check:
985-
//
986-
// func foo(_ x: @autoclosure () -> Int) {}
987-
// func bar(_ y: @autoclosure () -> Int) {
988-
// foo(y)
989-
// }
990-
auto isAutoClosureArg = [&](Expr *anchor, unsigned argIdx) -> bool {
991-
assert(anchor);
992-
993-
auto *argExpr = getArgumentExpr(anchor, argIdx);
994-
if (!argExpr)
995-
return false;
996-
997-
if (auto *DRE = dyn_cast<DeclRefExpr>(argExpr)) {
998-
if (auto *param = dyn_cast<ParamDecl>(DRE->getDecl()))
999-
return param->isAutoClosure();
1000-
}
1001-
1002-
return false;
1003-
};
1004-
1005981
for (unsigned paramIdx = 0, numParams = parameterBindings.size();
1006982
paramIdx != numParams; ++paramIdx){
1007983
// Skip unfulfilled parameters. There's nothing to do for them.
@@ -1012,29 +988,33 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
1012988
const auto &param = params[paramIdx];
1013989
auto paramTy = param.getOldType();
1014990

1015-
if (param.isAutoClosure())
1016-
paramTy = paramTy->castTo<FunctionType>()->getResult();
1017-
1018991
// Compare each of the bound arguments for this parameter.
1019992
for (auto argIdx : parameterBindings[paramIdx]) {
1020993
auto loc = locator.withPathElement(LocatorPathElt::
1021994
getApplyArgToParam(argIdx,
1022995
paramIdx));
1023996
auto argTy = argsWithLabels[argIdx].getOldType();
1024997

1025-
// If parameter was marked as `@autoclosure` and argument
1026-
// is itself `@autoclosure` function type in Swift < 5,
1027-
// let's fix that up by making it look like argument is
1028-
// called implicitly.
1029-
if (param.isAutoClosure() &&
1030-
isAutoClosureArg(locator.getAnchor(), argIdx)) {
1031-
argTy = argTy->castTo<FunctionType>()->getResult();
1032-
cs.increaseScore(SK_FunctionConversion);
1033-
1034-
if (cs.getASTContext().isSwiftVersionAtLeast(5)) {
1035-
auto *fixLoc = cs.getConstraintLocator(loc);
1036-
if (cs.recordFix(AutoClosureForwarding::create(cs, fixLoc)))
1037-
return cs.getTypeMatchFailure(loc);
998+
bool matchingAutoClosureResult = param.isAutoClosure();
999+
if (param.isAutoClosure()) {
1000+
auto &ctx = cs.getASTContext();
1001+
auto *fnType = paramTy->castTo<FunctionType>();
1002+
auto *argExpr = getArgumentExpr(locator.getAnchor(), argIdx);
1003+
1004+
// If the argument is not marked as @autoclosure or
1005+
// this is Swift version >= 5 where forwarding is not allowed,
1006+
// argument would always be wrapped into an implicit closure
1007+
// at the end, so we can safely match against result type.
1008+
if (ctx.isSwiftVersionAtLeast(5) || !isAutoClosureArgument(argExpr)) {
1009+
// In Swift >= 5 mode there is no @autoclosure forwarding,
1010+
// so let's match result types.
1011+
paramTy = fnType->getResult();
1012+
} else {
1013+
// Matching @autoclosure argument to @autoclosure parameter
1014+
// directly would mean introducting a function conversion
1015+
// in Swift <= 4 mode.
1016+
cs.increaseScore(SK_FunctionConversion);
1017+
matchingAutoClosureResult = false;
10381018
}
10391019
}
10401020

@@ -1045,7 +1025,7 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
10451025

10461026
cs.addConstraint(
10471027
subKind, argTy, paramTy,
1048-
param.isAutoClosure()
1028+
matchingAutoClosureResult
10491029
? loc.withPathElement(ConstraintLocator::AutoclosureResult)
10501030
: loc,
10511031
/*isFavored=*/false);
@@ -2192,6 +2172,18 @@ bool ConstraintSystem::repairFailures(
21922172
break;
21932173
}
21942174

2175+
case ConstraintLocator::FunctionResult: {
2176+
// `apply argument` -> `arg/param compare` ->
2177+
// `@autoclosure result` -> `function result`
2178+
if (path.size() > 3) {
2179+
const auto &elt = path[path.size() - 2];
2180+
if (elt.getKind() == ConstraintLocator::AutoclosureResult &&
2181+
repairByInsertingExplicitCall(lhs, rhs))
2182+
return true;
2183+
}
2184+
break;
2185+
}
2186+
21952187
case ConstraintLocator::AutoclosureResult: {
21962188
if (repairByInsertingExplicitCall(lhs, rhs))
21972189
return true;
@@ -6565,6 +6557,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
65656557
return result;
65666558
}
65676559

6560+
case FixKind::AutoClosureForwarding: {
6561+
if (recordFix(fix))
6562+
return SolutionKind::Error;
6563+
return matchTypes(type1, type2, matchKind, subflags, locator);
6564+
}
6565+
65686566
case FixKind::InsertCall:
65696567
case FixKind::RemoveReturn:
65706568
case FixKind::RemoveAddressOf:
@@ -6580,7 +6578,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
65806578
case FixKind::CoerceToCheckedCast:
65816579
case FixKind::RelabelArguments:
65826580
case FixKind::AddConformance:
6583-
case FixKind::AutoClosureForwarding:
65846581
case FixKind::RemoveUnwrap:
65856582
case FixKind::DefineMemberBasedOnUse:
65866583
case FixKind::AllowTypeOrInstanceMember:

lib/Sema/ConstraintSystem.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,6 +2666,18 @@ Expr *constraints::getArgumentExpr(Expr *expr, unsigned index) {
26662666
return cast<TupleExpr>(argExpr)->getElement(index);
26672667
}
26682668

2669+
bool constraints::isAutoClosureArgument(Expr *argExpr) {
2670+
if (!argExpr)
2671+
return false;
2672+
2673+
if (auto *DRE = dyn_cast<DeclRefExpr>(argExpr)) {
2674+
if (auto *param = dyn_cast<ParamDecl>(DRE->getDecl()))
2675+
return param->isAutoClosure();
2676+
}
2677+
2678+
return false;
2679+
}
2680+
26692681
void ConstraintSystem::generateConstraints(
26702682
SmallVectorImpl<Constraint *> &constraints, Type type,
26712683
ArrayRef<OverloadChoice> choices, DeclContext *useDC,

lib/Sema/ConstraintSystem.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3793,6 +3793,17 @@ Expr *simplifyLocatorToAnchor(ConstraintSystem &cs, ConstraintLocator *locator);
37933793
/// wasn't of one of the kinds listed above.
37943794
Expr *getArgumentExpr(Expr *expr, unsigned index);
37953795

3796+
// Check whether argument of the call at given position refers to
3797+
// parameter marked as `@autoclosure`. This function is used to
3798+
// maintain source compatibility with Swift versions < 5,
3799+
// previously examples like following used to type-check:
3800+
//
3801+
// func foo(_ x: @autoclosure () -> Int) {}
3802+
// func bar(_ y: @autoclosure () -> Int) {
3803+
// foo(y)
3804+
// }
3805+
bool isAutoClosureArgument(Expr *argExpr);
3806+
37963807
class DisjunctionChoice {
37973808
unsigned Index;
37983809
Constraint *Choice;

test/Compatibility/attr_autoclosure.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,10 @@ func passAutoClosureToEnumCase(_ fn: @escaping @autoclosure () -> Int) {
6262
// somewhere SILGen if `fn` doesn't have `@escaping`.
6363
let _: E = .baz(fn) // Ok
6464
}
65+
66+
do {
67+
func bar(_ fn: @autoclosure () -> (() -> Int)) {}
68+
func foo(_ fn: @autoclosure @escaping () -> (() -> Int)) {
69+
bar(fn) // Ok
70+
}
71+
}

test/attr/attr_autoclosure.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,3 +245,32 @@ protocol P_47586626 {
245245
func foo(_: @autoclosure F)
246246
func bar<T>(_: @autoclosure G<T>)
247247
}
248+
249+
func overloaded_autoclj<T>(_: @autoclosure () -> T) {}
250+
func overloaded_autoclj(_: @autoclosure () -> Int) {}
251+
252+
func autoclosure_param_returning_func_type() {
253+
func foo(_ fn: @autoclosure () -> (() -> Int)) {}
254+
func generic_foo<T>(_ fn: @autoclosure () -> T) {}
255+
256+
func bar_1(_ fn: @autoclosure @escaping () -> Int) { foo(fn) } // Ok
257+
func bar_2(_ fn: @autoclosure () -> Int) { foo(fn) } // expected-note {{parameter 'fn' is implicitly non-escaping}}
258+
// expected-error@-1 {{using non-escaping parameter 'fn' in a context expecting an @escaping closure}}
259+
func baz_1(_ fn: @autoclosure @escaping () -> Int) { generic_foo(fn) } // Ok (T is inferred as () -> Int)
260+
func baz_2(_ fn: @autoclosure @escaping () -> Int) { generic_foo(fn()) } // Ok (T is inferred as Int)
261+
func baz_3(_ fn: @autoclosure () -> Int) { generic_foo(fn) } // Fails because fn is not marked as @escaping
262+
// expected-error@-1 {{converting non-escaping value to 'T' may allow it to escape}}
263+
264+
// Let's make sure using `fn` as value works fine in presence of overloading
265+
func biz_1(_ fn: @autoclosure @escaping () -> Int) { overloaded_autoclj(fn) } // Ok
266+
func biz_2(_ fn: @autoclosure @escaping () -> Int) { overloaded_autoclj(fn()) } // Ok
267+
func biz_3(_ fn: @autoclosure () -> Int) { overloaded_autoclj(fn) } // Fails because fn is not marked as @escaping
268+
// expected-error@-1 {{add () to forward @autoclosure parameter}} {{67-67=()}}
269+
270+
func fiz(_: @autoclosure () -> (() -> Int)) {}
271+
272+
func biz_4(_ fn: @autoclosure @escaping () -> (() -> Int)) { fiz(fn) } // Can't forward in Swift >= 5 mode
273+
// expected-error@-1 {{add () to forward @autoclosure parameter}} {{70-70=()}}
274+
func biz_5(_ fn: @escaping () -> (() -> Int)) { fiz(fn) } // Can't forward in Swift >= 5 mode
275+
// expected-error@-1 {{add () to forward @autoclosure parameter}} {{57-57=()}}
276+
}

0 commit comments

Comments
 (0)