@@ -1546,9 +1546,45 @@ static bool isMatchedAnyToAnyObjectConversion(CanType from, CanType to) {
1546
1546
return false ;
1547
1547
}
1548
1548
1549
+ static Conversion withNewInputType (const Conversion &conv,
1550
+ AbstractionPattern origType,
1551
+ CanType substType,
1552
+ SILType loweredType) {
1553
+ switch (conv.getKind ()) {
1554
+ case Conversion::Reabstract:
1555
+ return Conversion::getReabstract (origType, substType, loweredType,
1556
+ conv.getReabstractionOutputOrigType (),
1557
+ conv.getReabstractionOutputSubstType (),
1558
+ conv.getReabstractionOutputLoweredType ());
1559
+ case Conversion::Subtype:
1560
+ return Conversion::getSubtype (substType, conv.getBridgingResultType (),
1561
+ conv.getBridgingLoweredResultType ());
1562
+ default :
1563
+ llvm_unreachable (" shouldn't be trying to combine these kinds" );
1564
+ }
1565
+ }
1566
+
1567
+ static Conversion withNewOutputType (const Conversion &conv,
1568
+ AbstractionPattern origType,
1569
+ CanType substType,
1570
+ SILType loweredType) {
1571
+ switch (conv.getKind ()) {
1572
+ case Conversion::Reabstract:
1573
+ return Conversion::getReabstract (conv.getReabstractionInputOrigType (),
1574
+ conv.getReabstractionInputSubstType (),
1575
+ conv.getReabstractionInputLoweredType (),
1576
+ origType, substType, loweredType);
1577
+ case Conversion::Subtype:
1578
+ return Conversion::getSubtype (conv.getBridgingSourceType (),
1579
+ substType, loweredType);
1580
+ default :
1581
+ llvm_unreachable (" shouldn't be trying to combine these kinds" );
1582
+ }
1583
+ }
1584
+
1549
1585
// / Can a sequence of conversions from type1 -> type2 -> type3 be represented
1550
1586
// / as a conversion from type1 -> type3, or does that lose critical information?
1551
- static bool isPeepholeableConversionImpl (CanType type1,
1587
+ static bool isCombinableConversionImpl (CanType type1,
1552
1588
CanType type2,
1553
1589
CanType type3) {
1554
1590
if (type1 == type2 || type2 == type3) return true ;
@@ -1563,12 +1599,12 @@ static bool isPeepholeableConversionImpl(CanType type1,
1563
1599
// If we have optional -> optional conversions at both stages,
1564
1600
// look through them all.
1565
1601
if (auto object1 = type1.getOptionalObjectType ()) {
1566
- return isPeepholeableConversionImpl (object1, object2, object3);
1602
+ return isCombinableConversionImpl (object1, object2, object3);
1567
1603
1568
1604
// If we have an injection in the first stage, we'll still know we have
1569
1605
// an injection in the overall conversion.
1570
1606
} else {
1571
- return isPeepholeableConversionImpl (type1, object2, object3);
1607
+ return isCombinableConversionImpl (type1, object2, object3);
1572
1608
}
1573
1609
1574
1610
// We have an injection in the second stage. If we lose optionality
@@ -1580,7 +1616,7 @@ static bool isPeepholeableConversionImpl(CanType type1,
1580
1616
1581
1617
// Otherwise, we're preserving that we have an injection overall.
1582
1618
} else {
1583
- return isPeepholeableConversionImpl (type1, type2, object3);
1619
+ return isCombinableConversionImpl (type1, type2, object3);
1584
1620
}
1585
1621
}
1586
1622
@@ -1620,9 +1656,9 @@ static bool isPeepholeableConversionImpl(CanType type1,
1620
1656
assert (tuple1->getNumElements () == tuple3->getNumElements ());
1621
1657
assert (tuple2->getNumElements () == tuple3->getNumElements ());
1622
1658
for (auto i : range (tuple3->getNumElements ())) {
1623
- if (!isPeepholeableConversionImpl (tuple1.getElementType (i),
1624
- tuple2.getElementType (i),
1625
- tuple3.getElementType (i)))
1659
+ if (!isCombinableConversionImpl (tuple1.getElementType (i),
1660
+ tuple2.getElementType (i),
1661
+ tuple3.getElementType (i)))
1626
1662
return false ;
1627
1663
}
1628
1664
return true ;
@@ -1633,15 +1669,15 @@ static bool isPeepholeableConversionImpl(CanType type1,
1633
1669
auto fn1 = cast<AnyFunctionType>(type1);
1634
1670
assert (fn1->getNumParams () == fn3->getNumParams ());
1635
1671
assert (fn2->getNumParams () == fn3->getNumParams ());
1636
- if (!isPeepholeableConversionImpl (fn1.getResult (),
1637
- fn2.getResult (),
1638
- fn3.getResult ()))
1672
+ if (!isCombinableConversionImpl (fn1.getResult (),
1673
+ fn2.getResult (),
1674
+ fn3.getResult ()))
1639
1675
return false ;
1640
1676
for (auto i : range (fn3->getNumParams ())) {
1641
1677
// Note the reversal for invariance.
1642
- if (!isPeepholeableConversionImpl (fn3.getParams ()[i].getParameterType (),
1643
- fn2.getParams ()[i].getParameterType (),
1644
- fn1.getParams ()[i].getParameterType ()))
1678
+ if (!isCombinableConversionImpl (fn3.getParams ()[i].getParameterType (),
1679
+ fn2.getParams ()[i].getParameterType (),
1680
+ fn1.getParams ()[i].getParameterType ()))
1645
1681
return false ;
1646
1682
}
1647
1683
return true ;
@@ -1650,9 +1686,9 @@ static bool isPeepholeableConversionImpl(CanType type1,
1650
1686
if (auto exp3 = dyn_cast<PackExpansionType>(type3)) {
1651
1687
auto exp2 = cast<PackExpansionType>(type2);
1652
1688
auto exp1 = cast<PackExpansionType>(type1);
1653
- return isPeepholeableConversionImpl (exp1.getPatternType (),
1654
- exp2.getPatternType (),
1655
- exp3.getPatternType ());
1689
+ return isCombinableConversionImpl (exp1.getPatternType (),
1690
+ exp2.getPatternType (),
1691
+ exp3.getPatternType ());
1656
1692
}
1657
1693
1658
1694
// The only remaining types that support subtyping are classes and
@@ -1662,12 +1698,61 @@ static bool isPeepholeableConversionImpl(CanType type1,
1662
1698
1663
1699
// / Can we combine the given conversions so that we go straight from
1664
1700
// / innerSrcType to outerDestType, or does that lose information?
1665
- static bool isPeepholeableConversion (CanType innerSrcType, CanType innerDestType ,
1666
- CanType outerSrcType, CanType outerDestType ) {
1667
- assert (innerDestType == outerSrcType &&
1701
+ static bool isCombinableConversion ( const Conversion &inner ,
1702
+ const Conversion &outer ) {
1703
+ assert (inner. getResultType () == outer. getSourceType () &&
1668
1704
" unexpected intermediate conversion" );
1669
1705
1670
- return isPeepholeableConversionImpl (innerSrcType, innerDestType, outerDestType);
1706
+ return isCombinableConversionImpl (inner.getSourceType (),
1707
+ inner.getResultType (),
1708
+ outer.getResultType ());
1709
+ }
1710
+
1711
+ // / Given that we cannot combine the given conversions, at least
1712
+ // / "salvage" them to propagate semantically-critical contextual
1713
+ // / type information inward.
1714
+ static std::optional<CombinedConversions>
1715
+ salvageUncombinableConversion (SILGenFunction &SGF,
1716
+ const Conversion &inner,
1717
+ const Conversion &outer) {
1718
+ // If the outer type is `@isolated(any)`, and the intermediate type
1719
+ // is non-isolated, propagate the `@isolated(any)` conversion inwards.
1720
+ // We don't want to do this if the intermediate function has some
1721
+ // explicit isolation because we need to honor that conversion even
1722
+ // if it's not the formal isolation of the source function (e.g. if
1723
+ // the user coerces a nonisolated function to a @MainActor function
1724
+ // type). But if the intermediate function type is non-isolated, the
1725
+ // actual closure might still be isolated, either because we're
1726
+ // type-checking in some mode that doesn't propagate isolation in types
1727
+ // or because the isolation isn't representable in the type system
1728
+ // (e.g. it's isolated to some capture).
1729
+ if (auto outerOutputFnType =
1730
+ dyn_cast<AnyFunctionType>(outer.getResultType ())) {
1731
+ auto intermediateFnType = cast<AnyFunctionType>(outer.getSourceType ());
1732
+ if (outerOutputFnType->getIsolation ().isErased () &&
1733
+ intermediateFnType->getIsolation ().isNonIsolated ()) {
1734
+ // Construct new intermediate orig/subst/lowered types that are
1735
+ // just the old intermediate type with `@isolated(any)`.
1736
+ auto newIntermediateSubstType = intermediateFnType.withExtInfo (
1737
+ intermediateFnType->getExtInfo ().withIsolation (
1738
+ FunctionTypeIsolation::forErased ()));
1739
+ auto newIntermediateOrigType =
1740
+ AbstractionPattern (newIntermediateSubstType);
1741
+ auto newIntermediateLoweredType =
1742
+ SGF.getLoweredType (newIntermediateSubstType);
1743
+
1744
+ // Construct the new conversions with the new intermediate type.
1745
+ return CombinedConversions (
1746
+ withNewOutputType (inner, newIntermediateOrigType,
1747
+ newIntermediateSubstType,
1748
+ newIntermediateLoweredType),
1749
+ withNewInputType (outer, newIntermediateOrigType,
1750
+ newIntermediateSubstType,
1751
+ newIntermediateLoweredType));
1752
+ }
1753
+ }
1754
+
1755
+ return std::nullopt;
1671
1756
}
1672
1757
1673
1758
static std::optional<CombinedConversions>
@@ -1676,11 +1761,8 @@ combineReabstract(SILGenFunction &SGF,
1676
1761
const Conversion &inner) {
1677
1762
// We can never combine conversions in a way that would lose information
1678
1763
// about the intermediate types.
1679
- if (!isPeepholeableConversion (inner.getReabstractionInputSubstType (),
1680
- inner.getReabstractionOutputSubstType (),
1681
- outer.getReabstractionInputSubstType (),
1682
- outer.getReabstractionOutputSubstType ()))
1683
- return std::nullopt;
1764
+ if (!isCombinableConversion (inner, outer))
1765
+ return salvageUncombinableConversion (SGF, inner, outer);
1684
1766
1685
1767
// Recognize when the whole conversion is an identity.
1686
1768
if (inner.getReabstractionInputLoweredType ().getObjectType () ==
@@ -1705,11 +1787,8 @@ combineSubtypeIntoReabstract(SILGenFunction &SGF,
1705
1787
const Conversion &inner) {
1706
1788
// We can never combine conversions in a way that would lose information
1707
1789
// about the intermediate types.
1708
- if (!isPeepholeableConversion (inner.getBridgingSourceType (),
1709
- inner.getBridgingResultType (),
1710
- outer.getReabstractionInputSubstType (),
1711
- outer.getReabstractionOutputSubstType ()))
1712
- return std::nullopt;
1790
+ if (!isCombinableConversion (inner, outer))
1791
+ return salvageUncombinableConversion (SGF, inner, outer);
1713
1792
1714
1793
auto inputSubstType = inner.getBridgingSourceType ();
1715
1794
auto inputOrigType = AbstractionPattern (inputSubstType);
@@ -1727,11 +1806,8 @@ combineSubtypeIntoReabstract(SILGenFunction &SGF,
1727
1806
static std::optional<CombinedConversions>
1728
1807
combineSubtype (SILGenFunction &SGF,
1729
1808
const Conversion &outer, const Conversion &inner) {
1730
- if (!isPeepholeableConversion (inner.getBridgingSourceType (),
1731
- inner.getBridgingResultType (),
1732
- outer.getBridgingSourceType (),
1733
- outer.getBridgingResultType ()))
1734
- return std::nullopt;
1809
+ if (!isCombinableConversion (inner, outer))
1810
+ return salvageUncombinableConversion (SGF, inner, outer);
1735
1811
1736
1812
return CombinedConversions (
1737
1813
Conversion::getSubtype (inner.getBridgingSourceType (),
0 commit comments