Skip to content

Commit 3e64f9d

Browse files
committed
Push isolated(any) conversions down through multiple levels of conversion.
I'm not sure there's any way to test this right now. We don't naturally emit multiple function conversions on a single operand, and the only way to get a similar effect is to coerce, which interrupts the application of `@_inheritActorContext`. So I think this is dead code until we add closure isolation controls, and even then it might be dead unless we allow coercion of isolated closures, which maybe we won't. But it's the right thing to do in the abstract, and I was thinking of it now.
1 parent a040df9 commit 3e64f9d

File tree

1 file changed

+111
-35
lines changed

1 file changed

+111
-35
lines changed

lib/SILGen/SILGenConvert.cpp

Lines changed: 111 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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

16651750
static 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,
17191798
static std::optional<CombinedConversions>
17201799
combineSubtype(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

Comments
 (0)