@@ -903,11 +903,6 @@ namespace {
903
903
bool baseIsInstance) {
904
904
ValueDecl *member = choice.getDecl ();
905
905
906
- // FIXME: We should finish plumbing this through for dynamic
907
- // lookup as well.
908
- if (choice.getKind () == OverloadChoiceKind::DeclViaDynamic)
909
- return false ;
910
-
911
906
// If we're inside a selector expression, don't build the thunk.
912
907
// Were not actually going to emit the member reference, just
913
908
// look at the AST.
@@ -921,9 +916,11 @@ namespace {
921
916
if (!baseIsInstance && member->isInstanceMember ())
922
917
return true ;
923
918
924
- // Bound optional method references are represented via
925
- // DynamicMemberRefExpr instead of a curry thunk.
926
- if (member->getAttrs ().hasAttribute <OptionalAttr>())
919
+ // Bound member references that are '@objc optional' or found via dynamic
920
+ // lookup are always represented via DynamicMemberRefExpr instead of a
921
+ // curry thunk.
922
+ if (member->getAttrs ().hasAttribute <OptionalAttr>() ||
923
+ choice.getKind () == OverloadChoiceKind::DeclViaDynamic)
927
924
return false ;
928
925
929
926
// Figure out how many argument lists we need.
@@ -1002,7 +999,10 @@ namespace {
1002
999
auto *calleeFnTy = fnTy;
1003
1000
1004
1001
if (baseExpr) {
1002
+ // Coerce the base expression to the container type.
1005
1003
const auto calleeSelfParam = calleeFnTy->getParams ().front ();
1004
+ baseExpr =
1005
+ coerceToType (baseExpr, calleeSelfParam.getOldType (), locator);
1006
1006
1007
1007
// If the 'self' parameter has non-trivial ownership, adjust the
1008
1008
// argument type accordingly.
@@ -1159,7 +1159,33 @@ namespace {
1159
1159
Expr *thunkBody = buildSingleCurryThunkBodyCall (
1160
1160
baseExpr, fnExpr, declOrClosure, thunkParamList, locator);
1161
1161
1162
- // Coerce to the result type of the thunk.
1162
+ // If we called a function with a dynamic 'Self' result, we may need some
1163
+ // special handling.
1164
+ if (baseExpr) {
1165
+ if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(declOrClosure)) {
1166
+ if (fnDecl->hasDynamicSelfResult ()) {
1167
+ Type convTy;
1168
+
1169
+ if (cs.getType (baseExpr)->hasOpenedExistential ()) {
1170
+ // FIXME: Sometimes we need to convert to an opened existential
1171
+ // first, because CovariantReturnConversionExpr does not support
1172
+ // direct conversions from a class C to an existential C & P.
1173
+ convTy = cs.getType (baseExpr)->getMetatypeInstanceType ();
1174
+ convTy =
1175
+ thunkTy->getResult ()->replaceCovariantResultType (convTy, 0 );
1176
+ } else {
1177
+ convTy = thunkTy->getResult ();
1178
+ }
1179
+
1180
+ if (!thunkBody->getType ()->isEqual (convTy)) {
1181
+ thunkBody = cs.cacheType (
1182
+ new (ctx) CovariantReturnConversionExpr (thunkBody, convTy));
1183
+ }
1184
+ }
1185
+ }
1186
+ }
1187
+
1188
+ // Now, coerce to the result type of the thunk.
1163
1189
thunkBody = coerceToType (thunkBody, thunkTy->getResult (), locator);
1164
1190
1165
1191
if (thunkTy->getExtInfo ().isThrowing ()) {
@@ -1261,20 +1287,27 @@ namespace {
1261
1287
cs.cacheType (selfParamRef);
1262
1288
}
1263
1289
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.
1290
+ bool hasOpenedExistential = false ;
1269
1291
Expr *selfOpenedRef = selfParamRef;
1270
- if (selfCalleeParamTy->hasOpenedExistential ()) {
1292
+
1293
+ // If the 'self' parameter type is existential, it must be opened.
1294
+ if (selfThunkParamTy->isAnyExistentialType ()) {
1295
+ Type openedTy = solution.OpenedExistentialTypes .lookup (
1296
+ cs.getConstraintLocator (memberLocator));
1297
+ assert (openedTy);
1298
+
1299
+ hasOpenedExistential = true ;
1300
+
1271
1301
// If we're opening an existential:
1272
1302
// - The type of 'memberRef' inside the thunk is written in terms of the
1273
- // open existental archetype.
1303
+ // opened existental archetype.
1274
1304
// - The type of the thunk is written in terms of the
1275
1305
// erased existential bounds.
1276
- auto opaqueValueTy = selfCalleeParamTy;
1277
- if (selfCalleeParam.isInOut ())
1306
+ Type opaqueValueTy = openedTy;
1307
+ if (selfThunkParamTy->is <ExistentialMetatypeType>())
1308
+ opaqueValueTy = MetatypeType::get (opaqueValueTy);
1309
+
1310
+ if (selfThunkParam.isInOut ())
1278
1311
opaqueValueTy = LValueType::get (opaqueValueTy);
1279
1312
1280
1313
selfOpenedRef = new (ctx) OpaqueValueExpr (SourceLoc (), opaqueValueTy);
@@ -1292,6 +1325,9 @@ namespace {
1292
1325
// build a dynamic member reference. Otherwise, build a nested
1293
1326
// "{ args... in self.member(args...) }" thunk that calls the member.
1294
1327
if (isDynamicLookup || member->getAttrs ().hasAttribute <OptionalAttr>()) {
1328
+ auto *const selfCalleeTy =
1329
+ cs.getType (memberRef)->castTo <FunctionType>();
1330
+
1295
1331
outerThunkBody = new (ctx) DynamicMemberRefExpr (
1296
1332
selfOpenedRef, SourceLoc (),
1297
1333
resolveConcreteDeclRef (member, memberLocator), memberLoc);
@@ -1303,10 +1339,11 @@ namespace {
1303
1339
memberLocator);
1304
1340
1305
1341
// Close the existential if warranted.
1306
- if (selfCalleeParamTy-> hasOpenedExistential () ) {
1342
+ if (hasOpenedExistential) {
1307
1343
// If the callee's 'self' parameter has non-trivial ownership, adjust
1308
1344
// the argument type accordingly.
1309
- adjustExprOwnershipForParam (selfOpenedRef, selfCalleeParam);
1345
+ adjustExprOwnershipForParam (selfOpenedRef,
1346
+ selfCalleeTy->getParams ().front ());
1310
1347
1311
1348
outerThunkBody = new (ctx) OpenExistentialExpr (
1312
1349
selfParamRef, cast<OpaqueValueExpr>(selfOpenedRef),
@@ -1319,7 +1356,7 @@ namespace {
1319
1356
outerThunkTy->getResult ()->castTo <FunctionType>(), memberLocator);
1320
1357
1321
1358
// Rewrite the body to close the existential if warranted.
1322
- if (selfCalleeParamTy-> hasOpenedExistential () ) {
1359
+ if (hasOpenedExistential) {
1323
1360
auto *body = innerThunk->getSingleExpressionBody ();
1324
1361
body = new (ctx) OpenExistentialExpr (
1325
1362
selfParamRef, cast<OpaqueValueExpr>(selfOpenedRef), body,
@@ -1438,7 +1475,7 @@ namespace {
1438
1475
1439
1476
const bool isUnboundInstanceMember =
1440
1477
(!baseIsInstance && member->isInstanceMember ());
1441
- const bool isPartialApplication =
1478
+ const bool needsCurryThunk =
1442
1479
shouldBuildCurryThunk (choice, baseIsInstance);
1443
1480
1444
1481
// The formal type of the 'self' value for the member's declaration.
@@ -1452,24 +1489,28 @@ namespace {
1452
1489
// the base accordingly.
1453
1490
bool openedExistential = false ;
1454
1491
1455
- // For a partial application, we have to open the existential inside
1456
- // the thunk itself.
1457
1492
auto knownOpened = solution.OpenedExistentialTypes .find (
1458
1493
getConstraintSystem ().getConstraintLocator (
1459
1494
memberLocator));
1460
1495
if (knownOpened != solution.OpenedExistentialTypes .end ()) {
1461
1496
// Determine if we're going to have an OpenExistentialExpr around
1462
1497
// this member reference.
1463
1498
//
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.
1499
+ // For an unbound reference to a method, always open the existential
1500
+ // inside the curry thunk, because we won't have a 'self' value until
1501
+ // the curry thunk is applied.
1502
+ //
1503
+ // For a partial application of a protocol method, open the existential
1504
+ // inside the curry thunk as well. This reduces abstraction and
1505
+ // post-factum function type conversions, and results in better SILGen.
1468
1506
//
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 ()) {
1507
+ // For a partial application of a class method, however, we always want
1508
+ // the thunk to accept a class to avoid potential abstraction, so the
1509
+ // existential base must be opened eagerly in order to be upcast to the
1510
+ // appropriate class reference type before it is passed to the thunk.
1511
+ if (!needsCurryThunk ||
1512
+ (!member->getDeclContext ()->getSelfProtocolDecl () &&
1513
+ !isUnboundInstanceMember)) {
1473
1514
// Open the existential before performing the member reference.
1474
1515
base = openExistentialReference (base, knownOpened->second , member);
1475
1516
baseTy = knownOpened->second ;
@@ -1508,13 +1549,15 @@ namespace {
1508
1549
base, selfParamTy, member,
1509
1550
locator.withPathElement (ConstraintLocator::MemberRefBase));
1510
1551
} 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));
1552
+ // The base of an unbound reference is unused, and thus a conversion
1553
+ // is not necessary.
1554
+ if (!isUnboundInstanceMember) {
1555
+ if (!isExistentialMetatype || openedExistential) {
1556
+ // Convert the base to an rvalue of the appropriate metatype.
1557
+ base = coerceToType (
1558
+ base, MetatypeType::get (isDynamic ? selfTy : containerTy),
1559
+ locator.withPathElement (ConstraintLocator::MemberRefBase));
1560
+ }
1518
1561
}
1519
1562
1520
1563
if (!base)
@@ -1544,8 +1587,8 @@ namespace {
1544
1587
}
1545
1588
1546
1589
// Handle dynamic references.
1547
- if (isDynamic || (!isPartialApplication &&
1548
- member->getAttrs ().hasAttribute <OptionalAttr>())) {
1590
+ if (!needsCurryThunk &&
1591
+ (isDynamic || member->getAttrs ().hasAttribute <OptionalAttr>())) {
1549
1592
base = cs.coerceToRValue (base);
1550
1593
Expr *ref = new (context) DynamicMemberRefExpr (base, dotLoc, memberRef,
1551
1594
memberLoc);
@@ -1635,7 +1678,7 @@ namespace {
1635
1678
//
1636
1679
// { self in { args... in self.method(args...) } }(foo)
1637
1680
//
1638
- // This is done instead of just hoising the expression 'foo' up
1681
+ // This is done instead of just hoisting the expression 'foo' up
1639
1682
// into the closure, which would change evaluation order.
1640
1683
//
1641
1684
// However, for a super method reference, eg, 'let fn = super.foo',
@@ -1644,12 +1687,12 @@ namespace {
1644
1687
// very specific shape, we only emit a single closure here and
1645
1688
// capture the original SuperRefExpr, since its evaluation does not
1646
1689
// have side effects, instead of abstracting out a 'self' parameter.
1647
- const auto isSuperPartialApplication = isPartialApplication && isSuper;
1690
+ const auto isSuperPartialApplication = needsCurryThunk && isSuper;
1648
1691
if (isSuperPartialApplication) {
1649
1692
ref = buildSingleCurryThunk (base, declRefExpr,
1650
1693
cast<AbstractFunctionDecl>(member),
1651
1694
memberLocator);
1652
- } else if (isPartialApplication ) {
1695
+ } else if (needsCurryThunk ) {
1653
1696
// Another case where we want to build a single closure is when
1654
1697
// we have a partial application of a constructor on a statically-
1655
1698
// derived metatype value. Again, there are no order of evaluation
@@ -1672,17 +1715,28 @@ namespace {
1672
1715
return closure;
1673
1716
}
1674
1717
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>();
1718
+ FunctionType *curryThunkTy = nullptr ;
1719
+ if (isUnboundInstanceMember) {
1720
+ // For an unbound reference to a method, all conversions, including
1721
+ // dynamic 'Self' handling, are done within the thunk to support
1722
+ // the edge case of an unbound reference to a 'Self'-returning class
1723
+ // method on a protocol metatype. The result of calling the method
1724
+ // must be downcast to the opened archetype before being erased to the
1725
+ // subclass existential to cope with the expectations placed
1726
+ // on 'CovariantReturnConversionExpr'.
1727
+ curryThunkTy = simplifyType (openedType)->castTo <FunctionType>();
1728
+ } else {
1729
+ curryThunkTy = refTy->castTo <FunctionType>();
1730
+
1731
+ // Check if we need to open an existential stored inside 'self'.
1732
+ auto knownOpened = solution.OpenedExistentialTypes .find (
1733
+ getConstraintSystem ().getConstraintLocator (memberLocator));
1734
+ if (knownOpened != solution.OpenedExistentialTypes .end ()) {
1735
+ curryThunkTy =
1736
+ curryThunkTy
1737
+ ->typeEraseOpenedArchetypesWithRoot (knownOpened->second , dc)
1738
+ ->castTo <FunctionType>();
1739
+ }
1686
1740
}
1687
1741
1688
1742
// Replace the DeclRefExpr with a closure expression which SILGen
@@ -1695,7 +1749,10 @@ namespace {
1695
1749
// implicit function type conversion around the resulting expression,
1696
1750
// with the destination type having 'Self' swapped for the appropriate
1697
1751
// replacement type -- usually the base object type.
1698
- if (!member->getDeclContext ()->getSelfProtocolDecl ()) {
1752
+ //
1753
+ // Note: For unbound references this is handled inside the thunk.
1754
+ if (!isUnboundInstanceMember &&
1755
+ !member->getDeclContext ()->getSelfProtocolDecl ()) {
1699
1756
if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
1700
1757
if (func->hasDynamicSelfResult () &&
1701
1758
!baseTy->getOptionalObjectType ()) {
0 commit comments