Skip to content

Commit 0b5a803

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 196d47b commit 0b5a803

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
@@ -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

16731758
static 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,
17271806
static std::optional<CombinedConversions>
17281807
combineSubtype(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

Comments
 (0)