@@ -1546,9 +1546,45 @@ static bool isMatchedAnyToAnyObjectConversion(CanType from, CanType to) {
15461546 return false ;
15471547}
15481548
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+
15491585// / Can a sequence of conversions from type1 -> type2 -> type3 be represented
15501586// / as a conversion from type1 -> type3, or does that lose critical information?
1551- static bool isPeepholeableConversionImpl (CanType type1,
1587+ static bool isCombinableConversionImpl (CanType type1,
15521588 CanType type2,
15531589 CanType type3) {
15541590 if (type1 == type2 || type2 == type3) return true ;
@@ -1563,12 +1599,12 @@ static bool isPeepholeableConversionImpl(CanType type1,
15631599 // If we have optional -> optional conversions at both stages,
15641600 // look through them all.
15651601 if (auto object1 = type1.getOptionalObjectType ()) {
1566- return isPeepholeableConversionImpl (object1, object2, object3);
1602+ return isCombinableConversionImpl (object1, object2, object3);
15671603
15681604 // If we have an injection in the first stage, we'll still know we have
15691605 // an injection in the overall conversion.
15701606 } else {
1571- return isPeepholeableConversionImpl (type1, object2, object3);
1607+ return isCombinableConversionImpl (type1, object2, object3);
15721608 }
15731609
15741610 // We have an injection in the second stage. If we lose optionality
@@ -1580,7 +1616,7 @@ static bool isPeepholeableConversionImpl(CanType type1,
15801616
15811617 // Otherwise, we're preserving that we have an injection overall.
15821618 } else {
1583- return isPeepholeableConversionImpl (type1, type2, object3);
1619+ return isCombinableConversionImpl (type1, type2, object3);
15841620 }
15851621 }
15861622
@@ -1620,9 +1656,9 @@ static bool isPeepholeableConversionImpl(CanType type1,
16201656 assert (tuple1->getNumElements () == tuple3->getNumElements ());
16211657 assert (tuple2->getNumElements () == tuple3->getNumElements ());
16221658 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)))
16261662 return false ;
16271663 }
16281664 return true ;
@@ -1633,15 +1669,15 @@ static bool isPeepholeableConversionImpl(CanType type1,
16331669 auto fn1 = cast<AnyFunctionType>(type1);
16341670 assert (fn1->getNumParams () == fn3->getNumParams ());
16351671 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 ()))
16391675 return false ;
16401676 for (auto i : range (fn3->getNumParams ())) {
16411677 // 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 ()))
16451681 return false ;
16461682 }
16471683 return true ;
@@ -1650,9 +1686,9 @@ static bool isPeepholeableConversionImpl(CanType type1,
16501686 if (auto exp3 = dyn_cast<PackExpansionType>(type3)) {
16511687 auto exp2 = cast<PackExpansionType>(type2);
16521688 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 ());
16561692 }
16571693
16581694 // The only remaining types that support subtyping are classes and
@@ -1662,12 +1698,61 @@ static bool isPeepholeableConversionImpl(CanType type1,
16621698
16631699// / Can we combine the given conversions so that we go straight from
16641700// / 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 () &&
16681704 " unexpected intermediate conversion" );
16691705
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 ;
16711756}
16721757
16731758static std::optional<CombinedConversions>
@@ -1676,11 +1761,8 @@ combineReabstract(SILGenFunction &SGF,
16761761 const Conversion &inner) {
16771762 // We can never combine conversions in a way that would lose information
16781763 // 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);
16841766
16851767 // Recognize when the whole conversion is an identity.
16861768 if (inner.getReabstractionInputLoweredType ().getObjectType () ==
@@ -1705,11 +1787,8 @@ combineSubtypeIntoReabstract(SILGenFunction &SGF,
17051787 const Conversion &inner) {
17061788 // We can never combine conversions in a way that would lose information
17071789 // 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);
17131792
17141793 auto inputSubstType = inner.getBridgingSourceType ();
17151794 auto inputOrigType = AbstractionPattern (inputSubstType);
@@ -1727,11 +1806,8 @@ combineSubtypeIntoReabstract(SILGenFunction &SGF,
17271806static std::optional<CombinedConversions>
17281807combineSubtype (SILGenFunction &SGF,
17291808 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);
17351811
17361812 return CombinedConversions (
17371813 Conversion::getSubtype (inner.getBridgingSourceType (),
0 commit comments