@@ -1002,7 +1002,10 @@ namespace {
1002
1002
auto *calleeFnTy = fnTy;
1003
1003
1004
1004
if (baseExpr) {
1005
+ // Coerce the base expression to the container type.
1005
1006
const auto calleeSelfParam = calleeFnTy->getParams ().front ();
1007
+ baseExpr =
1008
+ coerceToType (baseExpr, calleeSelfParam.getOldType (), locator);
1006
1009
1007
1010
// If the 'self' parameter has non-trivial ownership, adjust the
1008
1011
// argument type accordingly.
@@ -1159,7 +1162,33 @@ namespace {
1159
1162
Expr *thunkBody = buildSingleCurryThunkBodyCall (
1160
1163
baseExpr, fnExpr, declOrClosure, thunkParamList, locator);
1161
1164
1162
- // Coerce to the result type of the thunk.
1165
+ // If we called a function with a dynamic 'Self' result, we may need some
1166
+ // special handling.
1167
+ if (baseExpr) {
1168
+ if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(declOrClosure)) {
1169
+ if (fnDecl->hasDynamicSelfResult ()) {
1170
+ Type convTy;
1171
+
1172
+ if (cs.getType (baseExpr)->hasOpenedExistential ()) {
1173
+ // FIXME: Sometimes we need to convert to an opened existential
1174
+ // first, because CovariantReturnConversionExpr does not support
1175
+ // direct conversions from a class C to an existential C & P.
1176
+ convTy = cs.getType (baseExpr)->getMetatypeInstanceType ();
1177
+ convTy =
1178
+ thunkTy->getResult ()->replaceCovariantResultType (convTy, 0 );
1179
+ } else {
1180
+ convTy = thunkTy->getResult ();
1181
+ }
1182
+
1183
+ if (!thunkBody->getType ()->isEqual (convTy)) {
1184
+ thunkBody = cs.cacheType (
1185
+ new (ctx) CovariantReturnConversionExpr (thunkBody, convTy));
1186
+ }
1187
+ }
1188
+ }
1189
+ }
1190
+
1191
+ // Now, coerce to the result type of the thunk.
1163
1192
thunkBody = coerceToType (thunkBody, thunkTy->getResult (), locator);
1164
1193
1165
1194
if (thunkTy->getExtInfo ().isThrowing ()) {
@@ -1261,20 +1290,27 @@ namespace {
1261
1290
cs.cacheType (selfParamRef);
1262
1291
}
1263
1292
1264
- auto *const selfCalleeTy = cs.getType (memberRef)->castTo <FunctionType>();
1265
- const auto selfCalleeParam = selfCalleeTy->getParams ().front ();
1266
- const auto selfCalleeParamTy = selfCalleeParam.getPlainType ();
1267
-
1268
- // Open the 'self' parameter reference if warranted.
1293
+ bool hasOpenedExistential = false ;
1269
1294
Expr *selfOpenedRef = selfParamRef;
1270
- if (selfCalleeParamTy->hasOpenedExistential ()) {
1295
+
1296
+ // If the 'self' parameter type is existential, it must be opened.
1297
+ if (selfThunkParamTy->isAnyExistentialType ()) {
1298
+ Type openedTy = solution.OpenedExistentialTypes .lookup (
1299
+ cs.getConstraintLocator (memberLocator));
1300
+ assert (openedTy);
1301
+
1302
+ hasOpenedExistential = true ;
1303
+
1271
1304
// If we're opening an existential:
1272
1305
// - The type of 'memberRef' inside the thunk is written in terms of the
1273
- // open existental archetype.
1306
+ // opened existental archetype.
1274
1307
// - The type of the thunk is written in terms of the
1275
1308
// erased existential bounds.
1276
- auto opaqueValueTy = selfCalleeParamTy;
1277
- if (selfCalleeParam.isInOut ())
1309
+ Type opaqueValueTy = openedTy;
1310
+ if (selfThunkParamTy->is <ExistentialMetatypeType>())
1311
+ opaqueValueTy = MetatypeType::get (opaqueValueTy);
1312
+
1313
+ if (selfThunkParam.isInOut ())
1278
1314
opaqueValueTy = LValueType::get (opaqueValueTy);
1279
1315
1280
1316
selfOpenedRef = new (ctx) OpaqueValueExpr (SourceLoc (), opaqueValueTy);
@@ -1292,6 +1328,9 @@ namespace {
1292
1328
// build a dynamic member reference. Otherwise, build a nested
1293
1329
// "{ args... in self.member(args...) }" thunk that calls the member.
1294
1330
if (isDynamicLookup || member->getAttrs ().hasAttribute <OptionalAttr>()) {
1331
+ auto *const selfCalleeTy =
1332
+ cs.getType (memberRef)->castTo <FunctionType>();
1333
+
1295
1334
outerThunkBody = new (ctx) DynamicMemberRefExpr (
1296
1335
selfOpenedRef, SourceLoc (),
1297
1336
resolveConcreteDeclRef (member, memberLocator), memberLoc);
@@ -1303,10 +1342,11 @@ namespace {
1303
1342
memberLocator);
1304
1343
1305
1344
// Close the existential if warranted.
1306
- if (selfCalleeParamTy-> hasOpenedExistential () ) {
1345
+ if (hasOpenedExistential) {
1307
1346
// If the callee's 'self' parameter has non-trivial ownership, adjust
1308
1347
// the argument type accordingly.
1309
- adjustExprOwnershipForParam (selfOpenedRef, selfCalleeParam);
1348
+ adjustExprOwnershipForParam (selfOpenedRef,
1349
+ selfCalleeTy->getParams ().front ());
1310
1350
1311
1351
outerThunkBody = new (ctx) OpenExistentialExpr (
1312
1352
selfParamRef, cast<OpaqueValueExpr>(selfOpenedRef),
@@ -1319,7 +1359,7 @@ namespace {
1319
1359
outerThunkTy->getResult ()->castTo <FunctionType>(), memberLocator);
1320
1360
1321
1361
// Rewrite the body to close the existential if warranted.
1322
- if (selfCalleeParamTy-> hasOpenedExistential () ) {
1362
+ if (hasOpenedExistential) {
1323
1363
auto *body = innerThunk->getSingleExpressionBody ();
1324
1364
body = new (ctx) OpenExistentialExpr (
1325
1365
selfParamRef, cast<OpaqueValueExpr>(selfOpenedRef), body,
@@ -1452,24 +1492,28 @@ namespace {
1452
1492
// the base accordingly.
1453
1493
bool openedExistential = false ;
1454
1494
1455
- // For a partial application, we have to open the existential inside
1456
- // the thunk itself.
1457
1495
auto knownOpened = solution.OpenedExistentialTypes .find (
1458
1496
getConstraintSystem ().getConstraintLocator (
1459
1497
memberLocator));
1460
1498
if (knownOpened != solution.OpenedExistentialTypes .end ()) {
1461
1499
// Determine if we're going to have an OpenExistentialExpr around
1462
1500
// this member reference.
1463
1501
//
1464
- // If we have a partial application of a protocol method, we open
1465
- // the existential in the curry thunk, instead of opening it here,
1466
- // because we won't have a 'self' value until the curry thunk is
1467
- // applied.
1502
+ // For an unbound reference to a method, always open the existential
1503
+ // inside the curry thunk, because we won't have a 'self' value until
1504
+ // the curry thunk is applied.
1505
+ //
1506
+ // For a partial application of a protocol method, open the existential
1507
+ // inside the curry thunk as well. This reduces abstraction and
1508
+ // post-factum function type conversions, and results in better SILGen.
1468
1509
//
1469
- // However, a partial application of a class method on a subclass
1470
- // existential does need to open the existential, so that it can be
1471
- // upcast to the appropriate class reference type.
1472
- if (!isPartialApplication || !containerTy->hasOpenedExistential ()) {
1510
+ // For a partial application of a class method, however, we always want
1511
+ // the thunk to accept a class to avoid potential abstraction, so the
1512
+ // existential base must be opened eagerly in order to be upcast to the
1513
+ // appropriate class reference type before it is passed to the thunk.
1514
+ if (!isPartialApplication ||
1515
+ (!member->getDeclContext ()->getSelfProtocolDecl () &&
1516
+ !isUnboundInstanceMember)) {
1473
1517
// Open the existential before performing the member reference.
1474
1518
base = openExistentialReference (base, knownOpened->second , member);
1475
1519
baseTy = knownOpened->second ;
@@ -1508,13 +1552,15 @@ namespace {
1508
1552
base, selfParamTy, member,
1509
1553
locator.withPathElement (ConstraintLocator::MemberRefBase));
1510
1554
} else {
1511
- if (!isExistentialMetatype || openedExistential) {
1512
- // Convert the base to an rvalue of the appropriate metatype.
1513
- base = coerceToType (base,
1514
- MetatypeType::get (
1515
- isDynamic ? selfTy : containerTy),
1516
- locator.withPathElement (
1517
- ConstraintLocator::MemberRefBase));
1555
+ // The base of an unbound reference is unused, and thus a conversion
1556
+ // is not necessary.
1557
+ if (!isUnboundInstanceMember) {
1558
+ if (!isExistentialMetatype || openedExistential) {
1559
+ // Convert the base to an rvalue of the appropriate metatype.
1560
+ base = coerceToType (
1561
+ base, MetatypeType::get (isDynamic ? selfTy : containerTy),
1562
+ locator.withPathElement (ConstraintLocator::MemberRefBase));
1563
+ }
1518
1564
}
1519
1565
1520
1566
if (!base)
@@ -1635,7 +1681,7 @@ namespace {
1635
1681
//
1636
1682
// { self in { args... in self.method(args...) } }(foo)
1637
1683
//
1638
- // This is done instead of just hoising the expression 'foo' up
1684
+ // This is done instead of just hoisting the expression 'foo' up
1639
1685
// into the closure, which would change evaluation order.
1640
1686
//
1641
1687
// However, for a super method reference, eg, 'let fn = super.foo',
@@ -1672,17 +1718,28 @@ namespace {
1672
1718
return closure;
1673
1719
}
1674
1720
1675
- auto *curryThunkTy = refTy->castTo <FunctionType>();
1676
-
1677
- // Check if we need to open an existential stored inside 'self'.
1678
- auto knownOpened = solution.OpenedExistentialTypes .find (
1679
- getConstraintSystem ().getConstraintLocator (
1680
- memberLocator));
1681
- if (knownOpened != solution.OpenedExistentialTypes .end ()) {
1682
- curryThunkTy = curryThunkTy
1683
- ->typeEraseOpenedArchetypesWithRoot (
1684
- knownOpened->second , dc)
1685
- ->castTo <FunctionType>();
1721
+ FunctionType *curryThunkTy = nullptr ;
1722
+ if (isUnboundInstanceMember) {
1723
+ // For an unbound reference to a method, all conversions, including
1724
+ // dynamic 'Self' handling, are done within the thunk to support
1725
+ // the edge case of an unbound reference to a 'Self'-returning class
1726
+ // method on a protocol metatype. The result of calling the method
1727
+ // must be downcast to the opened archetype before being erased to the
1728
+ // subclass existential to cope with the expectations placed
1729
+ // on 'CovariantReturnConversionExpr'.
1730
+ curryThunkTy = simplifyType (openedType)->castTo <FunctionType>();
1731
+ } else {
1732
+ curryThunkTy = refTy->castTo <FunctionType>();
1733
+
1734
+ // Check if we need to open an existential stored inside 'self'.
1735
+ auto knownOpened = solution.OpenedExistentialTypes .find (
1736
+ getConstraintSystem ().getConstraintLocator (memberLocator));
1737
+ if (knownOpened != solution.OpenedExistentialTypes .end ()) {
1738
+ curryThunkTy =
1739
+ curryThunkTy
1740
+ ->typeEraseOpenedArchetypesWithRoot (knownOpened->second , dc)
1741
+ ->castTo <FunctionType>();
1742
+ }
1686
1743
}
1687
1744
1688
1745
// Replace the DeclRefExpr with a closure expression which SILGen
@@ -1695,7 +1752,10 @@ namespace {
1695
1752
// implicit function type conversion around the resulting expression,
1696
1753
// with the destination type having 'Self' swapped for the appropriate
1697
1754
// replacement type -- usually the base object type.
1698
- if (!member->getDeclContext ()->getSelfProtocolDecl ()) {
1755
+ //
1756
+ // Note: For unbound references this is handled inside the thunk.
1757
+ if (!isUnboundInstanceMember &&
1758
+ !member->getDeclContext ()->getSelfProtocolDecl ()) {
1699
1759
if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
1700
1760
if (func->hasDynamicSelfResult () &&
1701
1761
!baseTy->getOptionalObjectType ()) {
0 commit comments