Skip to content

Commit 9ff4094

Browse files
committed
[region-isolation] Handle non-transferrable function arguments via an explicitly transferred parameter.
I also while doing this I replaced a bunch of places where we used to crash due to invariants failing to instead emit a "I don't know error" since it is more actionable for the user.
1 parent d77dede commit 9ff4094

File tree

3 files changed

+80
-17
lines changed

3 files changed

+80
-17
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,9 @@ ERROR(regionbasedisolation_transfer_yields_race_stronglytransferred_binding, non
910910
ERROR(regionbasedisolation_arg_transferred, none,
911911
"task isolated value of type %0 transferred to %1 context; later accesses to value could race",
912912
(Type, ActorIsolation))
913+
ERROR(regionbasedisolation_arg_passed_to_strongly_transferred_param, none,
914+
"task isolated value of type %0 passed as a strongly transferred parameter; later accesses could race",
915+
(Type))
913916
NOTE(regionbasedisolation_maybe_race, none,
914917
"access here could race", ())
915918
NOTE(regionbasedisolation_isolated_since_in_same_region_basename, none,

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,8 @@ void UseAfterTransferDiagnosticInferrer::init(const Operand *op) {
662662

663663
auto *autoClosureExpr = loc.getAsASTNode<AutoClosureExpr>();
664664
if (!autoClosureExpr) {
665-
llvm::report_fatal_error("Transfer error emission missing a case?!");
665+
diagnoseError(op->getUser(), diag::regionbasedisolation_unknown_pattern);
666+
return;
666667
}
667668

668669
auto *i = const_cast<SILInstruction *>(op->getUser());
@@ -710,6 +711,10 @@ class TransferNonTransferrableDiagnosticInferrer {
710711

711712
/// Used if we have a function argument that is transferred into an closure.
712713
FunctionArgumentClosure = 3,
714+
715+
/// Used if we have a function argument passed as an explicitly strongly
716+
/// transferring argument to a function.
717+
FunctionArgumentApplyStronglyTransferred = 4,
713718
};
714719

715720
struct UseDiagnosticInfo {
@@ -730,6 +735,11 @@ class TransferNonTransferrableDiagnosticInferrer {
730735
return {UseDiagnosticInfoKind::FunctionArgumentClosure, isolation};
731736
}
732737

738+
static UseDiagnosticInfo forFunctionArgumentApplyStronglyTransferred() {
739+
return {UseDiagnosticInfoKind::FunctionArgumentApplyStronglyTransferred,
740+
{}};
741+
}
742+
733743
private:
734744
UseDiagnosticInfo(UseDiagnosticInfoKind kind,
735745
std::optional<ActorIsolation> isolation)
@@ -747,7 +757,11 @@ class TransferNonTransferrableDiagnosticInferrer {
747757
: info(info),
748758
loc(info.transferredOperand->getUser()->getLoc().getSourceLoc()) {}
749759

750-
void run();
760+
/// Gathers diagnostics. Returns false if we emitted a "I don't understand
761+
/// error". If we emit such an error, we should bail without emitting any
762+
/// further diagnostics, since we may not have any diagnostics or be in an
763+
/// inconcistent state.
764+
bool run();
751765

752766
UseDiagnosticInfo getDiagnostic() const { return diagnosticInfo.value(); }
753767
SourceLoc getLoc() const { return loc; }
@@ -779,27 +793,55 @@ bool TransferNonTransferrableDiagnosticInferrer::initForIsolatedPartialApply(
779793
return false;
780794
}
781795

782-
void TransferNonTransferrableDiagnosticInferrer::run() {
783-
if (isa<SILFunctionArgument>(info.nonTransferrableValue)) {
784-
// We need to find the isolation info.
785-
auto loc = info.transferredOperand->getUser()->getLoc();
796+
bool TransferNonTransferrableDiagnosticInferrer::run() {
797+
if (!isa<SILFunctionArgument>(info.nonTransferrableValue)) {
798+
diagnosticInfo = UseDiagnosticInfo::forMiscUse();
799+
return true;
800+
}
786801

787-
if (auto *sourceApply = loc.getAsASTNode<ApplyExpr>()) {
788-
diagnosticInfo = UseDiagnosticInfo::forFunctionArgumentApply(
789-
sourceApply->getIsolationCrossing().value().getCalleeIsolation());
790-
return;
791-
}
802+
// We need to find the isolation info.
803+
auto loc = info.transferredOperand->getUser()->getLoc();
792804

793-
if (auto *ace = loc.getAsASTNode<AbstractClosureExpr>()) {
794-
if (ace->getActorIsolation().isActorIsolated()) {
795-
if (initForIsolatedPartialApply(info.transferredOperand, ace)) {
796-
return;
805+
if (auto *sourceApply = loc.getAsASTNode<ApplyExpr>()) {
806+
std::optional<ActorIsolation> isolation = {};
807+
808+
// First try to get the apply from the isolation crossing.
809+
if (auto value = sourceApply->getIsolationCrossing())
810+
isolation = value->getCalleeIsolation();
811+
812+
// If we could not infer an isolation...
813+
if (!isolation) {
814+
// First see if we have a transferring argument.
815+
if (auto fas = FullApplySite::isa(info.transferredOperand->getUser())) {
816+
if (fas.getArgumentParameterInfo(*info.transferredOperand)
817+
.hasOption(SILParameterInfo::Transferring)) {
818+
diagnosticInfo =
819+
UseDiagnosticInfo::forFunctionArgumentApplyStronglyTransferred();
820+
return true;
797821
}
798822
}
823+
824+
// Otherwise, emit a "we don't know error" that tells the user to file a
825+
// bug.
826+
diagnoseError(info.transferredOperand->getUser(),
827+
diag::regionbasedisolation_unknown_pattern);
828+
return false;
829+
}
830+
831+
diagnosticInfo = UseDiagnosticInfo::forFunctionArgumentApply(*isolation);
832+
return true;
833+
}
834+
835+
if (auto *ace = loc.getAsASTNode<AbstractClosureExpr>()) {
836+
if (ace->getActorIsolation().isActorIsolated()) {
837+
if (initForIsolatedPartialApply(info.transferredOperand, ace)) {
838+
return true;
839+
}
799840
}
800841
}
801842

802843
diagnosticInfo = UseDiagnosticInfo::forMiscUse();
844+
return true;
803845
}
804846

805847
//===----------------------------------------------------------------------===//
@@ -941,6 +983,8 @@ void TransferNonSendableImpl::runDiagnosticEvaluator() {
941983
workingPartition.print(llvm::dbgs()));
942984
}
943985

986+
LLVM_DEBUG(llvm::dbgs() << "Finished walking blocks for diagnostics.\n");
987+
944988
// Now that we have found all of our transferInsts/Requires emit errors.
945989
transferOpToRequireInstMultiMap.setFrozen();
946990
}
@@ -1065,18 +1109,21 @@ void TransferNonSendableImpl::emitTransferredNonTransferrableDiagnostics() {
10651109
if (transferredNonTransferrable.empty())
10661110
return;
10671111

1112+
LLVM_DEBUG(
1113+
llvm::dbgs() << "Emitting transfer non transferrable diagnostics.\n");
1114+
10681115
using UseDiagnosticInfoKind =
10691116
TransferNonTransferrableDiagnosticInferrer::UseDiagnosticInfoKind;
10701117

10711118
auto &astContext = regionInfo->getFunction()->getASTContext();
10721119
for (auto info : transferredNonTransferrable) {
10731120
auto *op = info.transferredOperand;
10741121
TransferNonTransferrableDiagnosticInferrer diagnosticInferrer(info);
1075-
diagnosticInferrer.run();
1122+
if (!diagnosticInferrer.run())
1123+
continue;
10761124

10771125
auto diagnosticInfo = diagnosticInferrer.getDiagnostic();
10781126
auto loc = diagnosticInferrer.getLoc();
1079-
10801127
switch (diagnosticInfo.kind) {
10811128
case UseDiagnosticInfoKind::Invalid:
10821129
llvm_unreachable("Should never see this");
@@ -1120,6 +1167,15 @@ void TransferNonSendableImpl::emitTransferredNonTransferrableDiagnostics() {
11201167
astContext, fArg->getDecl()->getLoc(),
11211168
diag::regionbasedisolation_isolated_since_in_same_region_basename,
11221169
"task isolated", fArg->getDecl()->getBaseName());
1170+
break;
1171+
}
1172+
case UseDiagnosticInfoKind::FunctionArgumentApplyStronglyTransferred: {
1173+
diagnoseError(
1174+
astContext, loc,
1175+
diag::regionbasedisolation_arg_passed_to_strongly_transferred_param,
1176+
op->get()->getType().getASTType())
1177+
.highlight(op->getUser()->getLoc().getSourceRange());
1178+
break;
11231179
}
11241180
}
11251181
}

test/Concurrency/transfernonsendable_warning_until_swift6.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,7 @@ func testIsolationError() async {
2828
func testArgumentError(_ x: NonSendableType) async { // expected-note {{value is task isolated since it is in the same region as 'x'}}
2929
await transferToMain(x) // expected-error {{task isolated value of type 'NonSendableType' transferred to main actor-isolated context; later accesses to value could race}}
3030
}
31+
32+
func testTransferringArgument(_ x: NonSendableType) async {
33+
transferValue(x) // expected-error {{task isolated value of type 'NonSendableType' passed as a strongly transferred parameter; later accesses could race}}
34+
}

0 commit comments

Comments
 (0)