Skip to content

Commit 61e0096

Browse files
committed
[region-isolation] Given a .none #isolation parameter, infer the isolation from the callee rather than the isolated parameter.
Otherwise, we will have differing isolation from other parameters since the isolations will look different since one will have the .none value as an instance and the other will not have one and instead will rely on the AST isolation info. That is the correct behavior here since we do not actually have an actor here. I also removed some undefined behavior in the merging code. The way the code should work is that we should check if the merge fails and in such a case emit an unknown pattern error... instead of not checking appropriately on the next iteration and hitting undefined behavior. rdar://130396399 (cherry picked from commit f43911b)
1 parent a2184be commit 61e0096

File tree

3 files changed

+74
-19
lines changed

3 files changed

+74
-19
lines changed

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,6 +1588,10 @@ class PartitionOpTranslator {
15881588
SmallVector<SILValue, 8> assignOperands;
15891589
SmallVector<SILValue, 8> assignResults;
15901590

1591+
// A helper we use to emit an unknown patten error if our merge is
1592+
// invalid. This ensures we guarantee that if we find an actor merge error,
1593+
// the compiler halts. Importantly this lets our users know 100% that if the
1594+
// compiler exits successfully, actor merge errors could not have happened.
15911595
std::optional<SILDynamicMergedIsolationInfo> mergedInfo;
15921596
if (resultIsolationInfoOverride) {
15931597
mergedInfo = resultIsolationInfoOverride;
@@ -1598,12 +1602,26 @@ class PartitionOpTranslator {
15981602
for (SILValue src : sourceValues) {
15991603
if (auto value = tryToTrackValue(src)) {
16001604
assignOperands.push_back(value->getRepresentative().getValue());
1601-
mergedInfo = mergedInfo->merge(value->getIsolationRegionInfo());
1605+
auto originalMergedInfo = mergedInfo;
1606+
(void)originalMergedInfo;
1607+
if (mergedInfo)
1608+
mergedInfo = mergedInfo->merge(value->getIsolationRegionInfo());
16021609

16031610
// If we fail to merge, then we have an incompatibility in between some
16041611
// of our arguments (consider isolated to different actors) or with the
16051612
// isolationInfo we specified. Emit an unknown patten error.
16061613
if (!mergedInfo) {
1614+
LLVM_DEBUG(
1615+
llvm::dbgs() << "Merge Failure!\n"
1616+
<< "Original Info: ";
1617+
if (originalMergedInfo)
1618+
originalMergedInfo->printForDiagnostics(llvm::dbgs());
1619+
else llvm::dbgs() << "nil";
1620+
llvm::dbgs() << "\nValue: "
1621+
<< value->getRepresentative().getValue();
1622+
llvm::dbgs() << "Value Info: ";
1623+
value->getIsolationRegionInfo().printForDiagnostics(llvm::dbgs());
1624+
llvm::dbgs() << "\n");
16071625
builder.addUnknownPatternError(src);
16081626
continue;
16091627
}
@@ -1612,7 +1630,7 @@ class PartitionOpTranslator {
16121630

16131631
for (SILValue result : resultValues) {
16141632
// If we had isolation info explicitly passed in... use our
1615-
// mergedInfo. Otherwise, we want to infer.
1633+
// resultIsolationInfoError. Otherwise, we want to infer.
16161634
if (resultIsolationInfoOverride) {
16171635
// We only get back result if it is non-Sendable.
16181636
if (auto nonSendableValue =
@@ -1647,9 +1665,10 @@ class PartitionOpTranslator {
16471665
// derived, introduce a fake element so we just propagate the actor
16481666
// region.
16491667
//
1650-
// NOTE: Here we check if we have mergedInfo rather than isolationInfo
1651-
// since we want to do this regardless of whether or not we passed in a
1652-
// specific isolation info unlike earlier when processing actual results.
1668+
// NOTE: Here we check if we have resultIsolationInfoOverride rather than
1669+
// isolationInfo since we want to do this regardless of whether or not we
1670+
// passed in a specific isolation info unlike earlier when processing
1671+
// actual results.
16531672
if (assignOperands.size() && resultIsolationInfoOverride) {
16541673
builder.addActorIntroducingInst(assignOperands.back(),
16551674
resultIsolationInfoOverride);

lib/SILOptimizer/Utils/SILIsolationInfo.cpp

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -363,21 +363,31 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
363363
}
364364

365365
if (auto *isolatedOp = fas.getIsolatedArgumentOperandOrNullPtr()) {
366-
// First pattern match from global actors being passed as isolated
367-
// parameters. This gives us better type information. If we can pattern
368-
// match... we should!
366+
// First see if we have an enum inst.
369367
if (auto *ei = dyn_cast<EnumInst>(isolatedOp->get())) {
370-
if (ei->getElement()->getParentEnum()->isOptionalDecl() &&
371-
ei->hasOperand()) {
372-
if (auto *ieri = dyn_cast<InitExistentialRefInst>(ei->getOperand())) {
373-
CanType selfASTType = ieri->getFormalConcreteType();
374-
375-
if (auto *nomDecl = selfASTType->getAnyActor()) {
376-
// The SILValue() parameter doesn't matter until we have isolation
377-
// history.
378-
if (nomDecl->isGlobalActor())
379-
return SILIsolationInfo::getGlobalActorIsolated(SILValue(),
380-
nomDecl);
368+
if (ei->getElement()->getParentEnum()->isOptionalDecl()) {
369+
// Pattern match from global actors being passed as isolated
370+
// parameters. This gives us better type information. If we can
371+
// pattern match... we should!
372+
if (ei->hasOperand()) {
373+
if (auto *ieri =
374+
dyn_cast<InitExistentialRefInst>(ei->getOperand())) {
375+
CanType selfASTType = ieri->getFormalConcreteType();
376+
377+
if (auto *nomDecl = selfASTType->getAnyActor()) {
378+
// The SILValue() parameter doesn't matter until we have
379+
// isolation history.
380+
if (nomDecl->isGlobalActor())
381+
return SILIsolationInfo::getGlobalActorIsolated(SILValue(),
382+
nomDecl);
383+
}
384+
}
385+
} else {
386+
// In this case, we have a .none so we are attempting to use the
387+
// global queue. In such a case, we need to not use the enum as our
388+
// value and instead need to grab the isolation of our apply.
389+
if (auto isolationInfo = get(fas.getCallee())) {
390+
return isolationInfo;
381391
}
382392
}
383393
}

test/Concurrency/transfernonsendable.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,3 +1758,29 @@ extension MyActor {
17581758
}
17591759
}
17601760

1761+
public struct TimeoutError: Error, CustomStringConvertible {
1762+
public var description: String { "Timed out" }
1763+
}
1764+
1765+
// We used to not merge the isolation below correctly causing us to emit a crash
1766+
// due to undefined behavior. Make sure we do not crash or emit an unhandled
1767+
// pattern error.
1768+
public func doNotCrashOrEmitUnhandledPatternErrors<T: Sendable>(
1769+
_ duration: Duration,
1770+
_ body: @escaping @Sendable () async throws -> T
1771+
) async throws -> T {
1772+
try await withThrowingTaskGroup(of: T.self) { taskGroup in
1773+
taskGroup.addTask {
1774+
try await Task.sleep(for: duration)
1775+
throw TimeoutError()
1776+
}
1777+
taskGroup.addTask {
1778+
return try await body()
1779+
}
1780+
for try await value in taskGroup {
1781+
taskGroup.cancelAll()
1782+
return value
1783+
}
1784+
throw CancellationError()
1785+
}
1786+
}

0 commit comments

Comments
 (0)