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