@@ -1538,9 +1538,45 @@ static bool isMatchedAnyToAnyObjectConversion(CanType from, CanType to) {
15381538 return false ;
15391539}
15401540
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+
15411577// / Can a sequence of conversions from type1 -> type2 -> type3 be represented
15421578// / as a conversion from type1 -> type3, or does that lose critical information?
1543- static bool isPeepholeableConversionImpl (CanType type1,
1579+ static bool isCombinableConversionImpl (CanType type1,
15441580 CanType type2,
15451581 CanType type3) {
15461582 if (type1 == type2 || type2 == type3) return true ;
@@ -1555,12 +1591,12 @@ static bool isPeepholeableConversionImpl(CanType type1,
15551591 // If we have optional -> optional conversions at both stages,
15561592 // look through them all.
15571593 if (auto object1 = type1.getOptionalObjectType ()) {
1558- return isPeepholeableConversionImpl (object1, object2, object3);
1594+ return isCombinableConversionImpl (object1, object2, object3);
15591595
15601596 // If we have an injection in the first stage, we'll still know we have
15611597 // an injection in the overall conversion.
15621598 } else {
1563- return isPeepholeableConversionImpl (type1, object2, object3);
1599+ return isCombinableConversionImpl (type1, object2, object3);
15641600 }
15651601
15661602 // We have an injection in the second stage. If we lose optionality
@@ -1572,7 +1608,7 @@ static bool isPeepholeableConversionImpl(CanType type1,
15721608
15731609 // Otherwise, we're preserving that we have an injection overall.
15741610 } else {
1575- return isPeepholeableConversionImpl (type1, type2, object3);
1611+ return isCombinableConversionImpl (type1, type2, object3);
15761612 }
15771613 }
15781614
@@ -1612,9 +1648,9 @@ static bool isPeepholeableConversionImpl(CanType type1,
16121648 assert (tuple1->getNumElements () == tuple3->getNumElements ());
16131649 assert (tuple2->getNumElements () == tuple3->getNumElements ());
16141650 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)))
16181654 return false ;
16191655 }
16201656 return true ;
@@ -1625,15 +1661,15 @@ static bool isPeepholeableConversionImpl(CanType type1,
16251661 auto fn1 = cast<AnyFunctionType>(type1);
16261662 assert (fn1->getNumParams () == fn3->getNumParams ());
16271663 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 ()))
16311667 return false ;
16321668 for (auto i : range (fn3->getNumParams ())) {
16331669 // 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 ()))
16371673 return false ;
16381674 }
16391675 return true ;
@@ -1642,9 +1678,9 @@ static bool isPeepholeableConversionImpl(CanType type1,
16421678 if (auto exp3 = dyn_cast<PackExpansionType>(type3)) {
16431679 auto exp2 = cast<PackExpansionType>(type2);
16441680 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 ());
16481684 }
16491685
16501686 // The only remaining types that support subtyping are classes and
@@ -1654,12 +1690,61 @@ static bool isPeepholeableConversionImpl(CanType type1,
16541690
16551691// / Can we combine the given conversions so that we go straight from
16561692// / 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 () &&
16601696 " unexpected intermediate conversion" );
16611697
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 ;
16631748}
16641749
16651750static std::optional<CombinedConversions>
@@ -1668,11 +1753,8 @@ combineReabstract(SILGenFunction &SGF,
16681753 const Conversion &inner) {
16691754 // We can never combine conversions in a way that would lose information
16701755 // 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);
16761758
16771759 // Recognize when the whole conversion is an identity.
16781760 if (inner.getReabstractionInputLoweredType ().getObjectType () ==
@@ -1697,11 +1779,8 @@ combineSubtypeIntoReabstract(SILGenFunction &SGF,
16971779 const Conversion &inner) {
16981780 // We can never combine conversions in a way that would lose information
16991781 // 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);
17051784
17061785 auto inputSubstType = inner.getBridgingSourceType ();
17071786 auto inputOrigType = AbstractionPattern (inputSubstType);
@@ -1719,11 +1798,8 @@ combineSubtypeIntoReabstract(SILGenFunction &SGF,
17191798static std::optional<CombinedConversions>
17201799combineSubtype (SILGenFunction &SGF,
17211800 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);
17271803
17281804 return CombinedConversions (
17291805 Conversion::getSubtype (inner.getBridgingSourceType (),
0 commit comments