Skip to content

Commit b06b66d

Browse files
committed
CSApply: Use 'buildSingleCurryThunk' instead of 'buildCurryThunk' where a double thunk is not needed
1 parent 9dccd33 commit b06b66d

File tree

1 file changed

+50
-30
lines changed

1 file changed

+50
-30
lines changed

lib/Sema/CSApply.cpp

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,29 @@ namespace {
11881188
thunkTy, locator);
11891189
}
11901190

1191+
/// Build a "{ args in base.fn(args) }" single-expression curry thunk.
1192+
///
1193+
/// \param baseExpr The base expression to be captured.
1194+
/// \param fnExpr The expression to be called by consecutively applying
1195+
/// the \p baseExpr and thunk parameters.
1196+
/// \param declOrClosure The underlying function-like declaration or
1197+
/// closure we're going to call.
1198+
/// \param locator The locator pinned on the function reference carried
1199+
/// by \p fnExpr. If the function has associated applied property wrappers,
1200+
/// the locator is used to pull them in.
1201+
AutoClosureExpr *buildSingleCurryThunk(Expr *baseExpr, Expr *fnExpr,
1202+
DeclContext *declOrClosure,
1203+
ConstraintLocatorBuilder locator) {
1204+
assert(baseExpr);
1205+
auto *const thunkTy = cs.getType(fnExpr)
1206+
->castTo<FunctionType>()
1207+
->getResult()
1208+
->castTo<FunctionType>();
1209+
1210+
return buildSingleCurryThunk(baseExpr, fnExpr, declOrClosure, thunkTy,
1211+
locator);
1212+
}
1213+
11911214
AutoClosureExpr *buildCurryThunk(ValueDecl *member,
11921215
FunctionType *selfFnTy,
11931216
Expr *selfParamRef,
@@ -1221,8 +1244,7 @@ namespace {
12211244
// FIXME: selfParamRef ownership
12221245

12231246
auto *const thunk = buildSingleCurryThunk(
1224-
selfOpenedRef, ref, dyn_cast<AbstractFunctionDecl>(member), selfFnTy,
1225-
locator);
1247+
selfOpenedRef, ref, cast<DeclContext>(member), selfFnTy, locator);
12261248

12271249
if (selfParam.getPlainType()->hasOpenedExistential()) {
12281250
auto *body = thunk->getSingleExpressionBody();
@@ -1516,56 +1538,54 @@ namespace {
15161538
cs.setType(declRefExpr, refTy);
15171539
Expr *ref = declRefExpr;
15181540

1541+
// A partial application thunk consists of two nested closures:
1542+
//
1543+
// { self in { args... in self.method(args...) } }
1544+
//
1545+
// If the reference has an applied 'self', eg 'let fn = foo.method',
1546+
// the outermost closure is wrapped inside a single ApplyExpr:
1547+
//
1548+
// { self in { args... in self.method(args...) } }(foo)
1549+
//
1550+
// This is done instead of just hoising the expression 'foo' up
1551+
// into the closure, which would change evaluation order.
1552+
//
1553+
// However, for a super method reference, eg, 'let fn = super.foo',
1554+
// the base expression is always a SuperRefExpr, possibly wrapped
1555+
// by an upcast. Since SILGen expects super method calls to have a
1556+
// very specific shape, we only emit a single closure here and
1557+
// capture the original SuperRefExpr, since its evaluation does not
1558+
// have side effects, instead of abstracting out a 'self' parameter.
15191559
const auto isSuperPartialApplication = isPartialApplication && isSuper;
15201560
if (isSuperPartialApplication) {
1521-
// A partial application thunk consists of two nested closures:
1522-
//
1523-
// { self in { args... in self.method(args...) } }
1524-
//
1525-
// If the reference has an applied 'self', eg 'let fn = foo.method',
1526-
// the outermost closure is wrapped inside a single ApplyExpr:
1527-
//
1528-
// { self in { args... in self.method(args...) } }(foo)
1529-
//
1530-
// This is done instead of just hoising the expression 'foo' up
1531-
// into the closure, which would change evaluation order.
1532-
//
1533-
// However, for a super method reference, eg, 'let fn = super.foo',
1534-
// the base expression is always a SuperRefExpr, possibly wrapped
1535-
// by an upcast. Since SILGen expects super method calls to have a
1536-
// very specific shape, we only emit a single closure here and
1537-
// capture the original SuperRefExpr, since its evaluation does not
1538-
// have side effects, instead of abstracting out a 'self' parameter.
1539-
const auto selfFnTy =
1540-
refTy->castTo<FunctionType>()->getResult()->castTo<FunctionType>();
1541-
1542-
ref = buildCurryThunk(member, selfFnTy, base, ref, memberLocator);
1561+
ref = buildSingleCurryThunk(base, declRefExpr,
1562+
cast<AbstractFunctionDecl>(member),
1563+
memberLocator);
15431564
} else if (isPartialApplication) {
1544-
auto curryThunkTy = refTy->castTo<FunctionType>();
1545-
15461565
// Another case where we want to build a single closure is when
15471566
// we have a partial application of a constructor on a statically-
15481567
// derived metatype value. Again, there are no order of evaluation
15491568
// concerns here, and keeping the call and base together in the AST
15501569
// improves SILGen.
15511570
if (isa<ConstructorDecl>(member) &&
15521571
cs.isStaticallyDerivedMetatype(base)) {
1553-
auto selfFnTy = curryThunkTy->getResult()->castTo<FunctionType>();
1554-
15551572
// Add a useless ".self" to avoid downstream diagnostics.
15561573
base = new (context) DotSelfExpr(base, SourceLoc(), base->getEndLoc(),
15571574
cs.getType(base));
15581575
cs.setType(base, base->getType());
15591576

1560-
auto closure = buildCurryThunk(member, selfFnTy, base, ref,
1561-
memberLocator);
1577+
auto *closure = buildSingleCurryThunk(
1578+
base, declRefExpr, cast<AbstractFunctionDecl>(member),
1579+
memberLocator);
15621580

15631581
// Skip the code below -- we're not building an extra level of
15641582
// call by applying the metatype; instead, the closure we just
15651583
// built is the curried reference.
15661584
return closure;
15671585
}
15681586

1587+
auto *curryThunkTy = refTy->castTo<FunctionType>();
1588+
15691589
// Check if we need to open an existential stored inside 'self'.
15701590
auto knownOpened = solution.OpenedExistentialTypes.find(
15711591
getConstraintSystem().getConstraintLocator(

0 commit comments

Comments
 (0)