Skip to content

Commit 0f415f5

Browse files
committed
[region-isolation] Perform checking of non-Sendable results using rbi rather than Sema.
In terms of the test suite the only difference is that we allow for non-Sendable types to be returned from nonisolated functions. This is safe due to the rules of rbi. We do still error when we return non-Sendable functions across isolation boundaries though. The reason that I am doing this now is that I am implementing a prototype that allows for nonisolated functions to inherit isolation from their caller. This would have required me to implement support both in Sema for results and arguments in SIL. Rather than implement results in Sema, I just finished the work of transitioning the result checking out of Sema and into SIL. The actual prototype will land in a subsequent change. rdar://127477211
1 parent ec85589 commit 0f415f5

File tree

7 files changed

+361
-74
lines changed

7 files changed

+361
-74
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,28 @@ NOTE(regionbasedisolation_out_sending_cannot_be_actor_isolated_note_named, none,
10981098
"returning %1 %0 risks causing data races since the caller assumes that %0 can be safely sent to other isolation domains",
10991099
(Identifier, StringRef))
11001100

1101+
//===
1102+
// non-Sendable Results
1103+
1104+
// Example: returning main-actor isolated result to a custom-actor isolated context risks causing data races
1105+
ERROR(rbi_isolation_crossing_result, none,
1106+
"non-Sendable %0-typed result can not be returned from %1 %kind2 to %3 context",
1107+
(Type, ActorIsolation, const ValueDecl *, ActorIsolation))
1108+
ERROR(rbi_isolation_crossing_result_no_decl, none,
1109+
"non-Sendable %0-typed result can not be returned from %1 function to %2 context",
1110+
(Type, ActorIsolation, ActorIsolation))
1111+
NOTE(rbi_non_sendable_nominal,none,
1112+
"%kind0 does not conform to the 'Sendable' protocol",
1113+
(const ValueDecl *))
1114+
NOTE(rbi_nonsendable_function_type,none,
1115+
"a function type must be marked '@Sendable' to conform to 'Sendable'", ())
1116+
NOTE(rbi_add_nominal_sendable_conformance,none,
1117+
"consider making %kind0 conform to the 'Sendable' protocol",
1118+
(const ValueDecl *))
1119+
NOTE(rbi_add_generic_parameter_sendable_conformance,none,
1120+
"consider making generic parameter %0 conform to the 'Sendable' protocol",
1121+
(Type))
1122+
11011123
//===----------------------------------------------------------------------===//
11021124
// MARK: Misc Diagnostics
11031125
//===----------------------------------------------------------------------===//

include/swift/SILOptimizer/Utils/PartitionOpError.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,8 @@ PARTITION_OP_ERROR(InOutSendingNotDisconnectedAtExit)
5959
/// to our user so that we can emit that error as we process.
6060
PARTITION_OP_ERROR(UnknownCodePattern)
6161

62+
/// Used to signify that an isolation crossing function is returning a
63+
/// non-Sendable value.
64+
PARTITION_OP_ERROR(NonSendableIsolationCrossingResult)
65+
6266
#undef PARTITION_OP_ERROR

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,14 @@ enum class PartitionOpKind : uint8_t {
462462
///
463463
/// Takes one parameter, the inout parameter that we need to check.
464464
InOutSendingAtFunctionExit,
465+
466+
/// This is the result of an isolation crossing apply site. We need to emit a
467+
/// special error since we never allow this.
468+
///
469+
/// DISCUSSION: This is actually just a form of "send". Sadly, we can not use
470+
/// "send" directly since "send" expects a SILOperand and these are values. So
471+
/// to work around the API issue, we have to use a different, specific entry.
472+
NonSendableIsolationCrossingResult,
465473
};
466474

467475
/// PartitionOp represents a primitive operation that can be performed on
@@ -574,6 +582,12 @@ class PartitionOp {
574582
sourceInst);
575583
}
576584

585+
static PartitionOp
586+
NonSendableIsolationCrossingResult(Element elt, SILInstruction *sourceInst) {
587+
return PartitionOp(PartitionOpKind::NonSendableIsolationCrossingResult, elt,
588+
sourceInst);
589+
}
590+
577591
bool operator==(const PartitionOp &other) const {
578592
return opKind == other.opKind && opArgs == other.opArgs &&
579593
source == other.source;
@@ -1053,6 +1067,22 @@ class PartitionOpError {
10531067
}
10541068
};
10551069

1070+
struct NonSendableIsolationCrossingResultError {
1071+
const PartitionOp *op;
1072+
1073+
Element returnValueElement;
1074+
1075+
NonSendableIsolationCrossingResultError(const PartitionOp &op,
1076+
Element returnValue)
1077+
: op(&op), returnValueElement(returnValue) {}
1078+
1079+
void print(llvm::raw_ostream &os, RegionAnalysisValueMap &valueMap) const;
1080+
1081+
SWIFT_DEBUG_DUMPER(dump(RegionAnalysisValueMap &valueMap)) {
1082+
print(llvm::dbgs(), valueMap);
1083+
}
1084+
};
1085+
10561086
#define PARTITION_OP_ERROR(NAME) \
10571087
static_assert(std::is_copy_constructible_v<NAME##Error>, \
10581088
#NAME " must be copy constructable");
@@ -1482,8 +1512,15 @@ struct PartitionOpEvaluator {
14821512

14831513
// Then emit an unknown code pattern error.
14841514
return handleError(UnknownCodePatternError(op));
1515+
case PartitionOpKind::NonSendableIsolationCrossingResult:
1516+
// Grab the dynamic dataflow isolation information for our element's
1517+
// region.
1518+
Region region = p.getRegion(op.getOpArgs()[0]);
1519+
1520+
// Then emit the error.
1521+
return handleError(
1522+
NonSendableIsolationCrossingResultError(op, op.getOpArgs()[0]));
14851523
}
1486-
14871524
llvm_unreachable("Covered switch isn't covered?!");
14881525
}
14891526

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,12 @@ struct PartitionOpBuilder {
15261526
PartitionOp::UnknownPatternError(lookupValueID(value), currentInst));
15271527
}
15281528

1529+
void addNonSendableIsolationCrossingResultError(SILValue value) {
1530+
currentInstPartitionOps.emplace_back(
1531+
PartitionOp::NonSendableIsolationCrossingResult(lookupValueID(value),
1532+
currentInst));
1533+
}
1534+
15291535
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
15301536

15311537
void print(llvm::raw_ostream &os) const;
@@ -2394,14 +2400,58 @@ class PartitionOpTranslator {
23942400
handleSILOperands(applySite.getOperandsWithoutIndirectResults());
23952401
}
23962402

2397-
// non-sendable results can't be returned from cross-isolation calls without
2398-
// a diagnostic emitted elsewhere. Here, give them a fresh value for better
2399-
// diagnostics hereafter
2403+
// Create a new assign fresh for each one of our values and unless our
2404+
// return value is sending, emit an extra error bit on the results that are
2405+
// non-Sendable.
24002406
SmallVector<SILValue, 8> applyResults;
24012407
getApplyResults(*applySite, applyResults);
2402-
for (auto result : applyResults)
2403-
if (auto value = tryToTrackValue(result))
2408+
2409+
auto substCalleeType = applySite.getSubstCalleeType();
2410+
2411+
// Today, all values in result info are sending or none are. So this is a
2412+
// safe to check that we have a sending result. In the future if we allow
2413+
// for sending to vary, then this code will need to be updated to vary with
2414+
// each result.
2415+
auto results = substCalleeType->getResults();
2416+
auto *applyExpr = applySite->getLoc().getAsASTNode<ApplyExpr>();
2417+
2418+
// We only emit the error if we do not have a sending result and if our
2419+
// callee isn't nonisolated.
2420+
//
2421+
// DISCUSSION: If our callee is non-isolated, we know that the value must
2422+
// have been returned as a non-sent disconnected value. The reason why this
2423+
// is different from a sending result is that the result may be part of the
2424+
// region of the operands while the sending result will not be. In either
2425+
// case though, we do not want to emit the error.
2426+
bool emitIsolationCrossingResultError =
2427+
(results.empty() || !results[0].hasOption(SILResultInfo::IsSending)) &&
2428+
// We have to check if we actually have an apply expr since we may not
2429+
// have one if we are processing a SIL test case since SIL does not have
2430+
// locations (which is how we grab our AST information).
2431+
!(applyExpr && applyExpr->getIsolationCrossing()
2432+
->getCalleeIsolation()
2433+
.isNonisolated());
2434+
2435+
for (auto result : applyResults) {
2436+
if (auto value = tryToTrackValue(result)) {
24042437
builder.addAssignFresh(value->getRepresentative().getValue());
2438+
if (emitIsolationCrossingResultError)
2439+
builder.addNonSendableIsolationCrossingResultError(
2440+
value->getRepresentative().getValue());
2441+
}
2442+
}
2443+
2444+
// If we are supposed to emit isolation crossing errors, go through our
2445+
// parameters and add the error on any indirect results that are
2446+
// non-Sendable.
2447+
if (emitIsolationCrossingResultError) {
2448+
for (auto result : applySite.getIndirectSILResults()) {
2449+
if (auto value = tryToTrackValue(result)) {
2450+
builder.addNonSendableIsolationCrossingResultError(
2451+
value->getRepresentative().getValue());
2452+
}
2453+
}
2454+
}
24052455
}
24062456

24072457
template <typename DestValues>

0 commit comments

Comments
 (0)