Skip to content

Commit c20abe5

Browse files
authored
Merge pull request #74919 from gottesmm/pr-d8b24a45ff257893c8172491f11a617fc00d5589
[region-isolation] Implement support for 'inout sending' diagnostics.
2 parents c2a87aa + 6fe7496 commit c20abe5

File tree

8 files changed

+560
-80
lines changed

8 files changed

+560
-80
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,9 @@ NOTE(sil_referencebinding_inout_binding_here, none,
943943

944944
NOTE(regionbasedisolation_maybe_race, none,
945945
"access can happen concurrently", ())
946+
NOTE(regionbasedisolation_inout_sending_must_be_reinitialized, none,
947+
"'inout sending' parameter must be reinitialized before function exit with a non-actor isolated value",
948+
())
946949
ERROR(regionbasedisolation_unknown_pattern, none,
947950
"pattern that the region based isolation checker does not understand how to check. Please file a bug",
948951
())
@@ -972,6 +975,9 @@ ERROR(regionbasedisolation_arg_passed_to_strongly_transferred_param, none,
972975
NOTE(regionbasedisolation_isolated_since_in_same_region_basename, none,
973976
"value is %0 since it is in the same region as %1",
974977
(StringRef, DeclBaseName))
978+
NOTE(regionbasedisolation_isolated_since_in_same_region_string, none,
979+
"%0 is %1since it is in the same region as %2",
980+
(Identifier, StringRef, Identifier))
975981

976982
//===---
977983
// New Transfer Non Sendable Diagnostics
@@ -981,6 +987,13 @@ ERROR(regionbasedisolation_named_transfer_yields_race, none,
981987
"sending %0 risks causing data races",
982988
(Identifier))
983989

990+
ERROR(regionbasedisolation_inout_sending_cannot_be_actor_isolated, none,
991+
"'inout sending' parameter %0 cannot be %1at end of function",
992+
(Identifier, StringRef))
993+
NOTE(regionbasedisolation_inout_sending_cannot_be_actor_isolated_note, none,
994+
"%1%0 risks causing races in between %1uses and caller uses since caller assumes value is not actor isolated",
995+
(Identifier, StringRef))
996+
984997
NOTE(regionbasedisolation_named_info_transfer_yields_race, none,
985998
"sending %1%0 to %2 callee risks causing data races between %2 and local %3 uses",
986999
(Identifier, StringRef, ActorIsolation, ActorIsolation))

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,15 @@ enum class PartitionOpKind : uint8_t {
410410
///
411411
/// This is used if we need to reject the program and do not want to assert.
412412
UnknownPatternError,
413+
414+
/// Require that a 'inout sending' parameter's region is not transferred and
415+
/// disconnected at a specific function exiting term inst.
416+
///
417+
/// This ensures that if users transfer away an inout sending parameter, the
418+
/// parameter is reinitialized with a disconnected value.
419+
///
420+
/// Takes one parameter, the inout parameter that we need to check.
421+
RequireInOutSendingAtFunctionExit,
413422
};
414423

415424
/// PartitionOp represents a primitive operation that can be performed on
@@ -494,6 +503,12 @@ class PartitionOp {
494503
return PartitionOp(PartitionOpKind::UnknownPatternError, elt, sourceInst);
495504
}
496505

506+
static PartitionOp
507+
RequireInOutSendingAtFunctionExit(Element elt, SILInstruction *sourceInst) {
508+
return PartitionOp(PartitionOpKind::RequireInOutSendingAtFunctionExit, elt,
509+
sourceInst);
510+
}
511+
497512
bool operator==(const PartitionOp &other) const {
498513
return opKind == other.opKind && opArgs == other.opArgs &&
499514
source == other.source;
@@ -925,6 +940,21 @@ struct PartitionOpEvaluator {
925940
isolationRegionInfo);
926941
}
927942

943+
/// Call our CRTP subclass.
944+
void handleInOutSendingNotInitializedAtExitError(
945+
const PartitionOp &op, Element elt, Operand *transferringOp) const {
946+
return asImpl().handleInOutSendingNotInitializedAtExitError(op, elt,
947+
transferringOp);
948+
}
949+
950+
/// Call our CRTP subclass.
951+
void handleInOutSendingNotDisconnectedAtExitError(
952+
const PartitionOp &op, Element elt,
953+
SILDynamicMergedIsolationInfo isolation) const {
954+
return asImpl().handleInOutSendingNotDisconnectedAtExitError(op, elt,
955+
isolation);
956+
}
957+
928958
/// Call isActorDerived on our CRTP subclass.
929959
bool isActorDerived(Element elt) const {
930960
return asImpl().isActorDerived(elt);
@@ -959,8 +989,10 @@ struct PartitionOpEvaluator {
959989
}
960990

961991
/// Overload of \p getIsolationRegionInfo without an Operand.
962-
SILIsolationInfo getIsolationRegionInfo(Region region) const {
963-
return getIsolationRegionInfo(region, nullptr).first;
992+
SILDynamicMergedIsolationInfo getIsolationRegionInfo(Region region) const {
993+
if (auto opt = getIsolationRegionInfo(region, nullptr))
994+
return opt->first;
995+
return SILDynamicMergedIsolationInfo();
964996
}
965997

966998
bool isTaskIsolatedDerived(Element elt) const {
@@ -1128,6 +1160,41 @@ struct PartitionOpEvaluator {
11281160
}
11291161
}
11301162
return;
1163+
case PartitionOpKind::RequireInOutSendingAtFunctionExit: {
1164+
assert(op.getOpArgs().size() == 1 &&
1165+
"Require PartitionOp should be passed 1 argument");
1166+
assert(p.isTrackingElement(op.getOpArgs()[0]) &&
1167+
"Require PartitionOp's argument should already be tracked");
1168+
1169+
// First check if the region of our 'inout sending' element has been
1170+
// transferred. In that case, we emit a special use after free error.
1171+
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[0])) {
1172+
for (auto transferredOperand : transferredOperandSet->data()) {
1173+
handleInOutSendingNotInitializedAtExitError(op, op.getOpArgs()[0],
1174+
transferredOperand);
1175+
}
1176+
return;
1177+
}
1178+
1179+
// If we were not transferred, check if our region is actor isolated. If
1180+
// so, error since we need a disconnected value in the inout parameter.
1181+
Region inoutSendingRegion = p.getRegion(op.getOpArgs()[0]);
1182+
auto dynamicRegionIsolation = getIsolationRegionInfo(inoutSendingRegion);
1183+
1184+
// If we failed to merge emit an unknown pattern error so we fail.
1185+
if (!dynamicRegionIsolation) {
1186+
handleUnknownCodePattern(op);
1187+
return;
1188+
}
1189+
1190+
// Otherwise, emit the error if the dynamic region isolation is not
1191+
// disconnected.
1192+
if (!dynamicRegionIsolation.isDisconnected()) {
1193+
handleInOutSendingNotDisconnectedAtExitError(op, op.getOpArgs()[0],
1194+
dynamicRegionIsolation);
1195+
}
1196+
return;
1197+
}
11311198
case PartitionOpKind::UnknownPatternError:
11321199
// Begin tracking the specified element in case we have a later use.
11331200
p.trackNewElement(op.getOpArgs()[0]);
@@ -1315,6 +1382,16 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
13151382
/// to our user so that we can emit that error as we process.
13161383
void handleUnknownCodePattern(const PartitionOp &op) const {}
13171384

1385+
/// Called if we find an 'inout sending' parameter that is not live at exit.
1386+
void handleInOutSendingNotInitializedAtExitError(
1387+
const PartitionOp &op, Element elt, Operand *transferringOp) const {}
1388+
1389+
/// Called if we find an 'inout sending' parameter that is live at excit but
1390+
/// is actor isolated instead of disconnected.
1391+
void handleInOutSendingNotDisconnectedAtExitError(
1392+
const PartitionOp &op, Element elt,
1393+
SILDynamicMergedIsolationInfo actorIsolation) const {}
1394+
13181395
/// This is used to determine if an element is actor derived. If we determine
13191396
/// that a region containing such an element is transferred, we emit an error
13201397
/// since actor regions cannot be transferred.

include/swift/SILOptimizer/Utils/SILIsolationInfo.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ class ActorInstance {
8181
return value.getPointer();
8282
}
8383

84+
SILValue maybeGetValue() const {
85+
if (getKind() != Kind::Value)
86+
return SILValue();
87+
return getValue();
88+
}
89+
8490
bool isValue() const { return getKind() == Kind::Value; }
8591

8692
bool isAccessorInit() const { return getKind() == Kind::ActorAccessorInit; }
@@ -389,7 +395,7 @@ class SILDynamicMergedIsolationInfo {
389395

390396
public:
391397
SILDynamicMergedIsolationInfo() : innerInfo() {}
392-
SILDynamicMergedIsolationInfo(SILIsolationInfo innerInfo)
398+
explicit SILDynamicMergedIsolationInfo(SILIsolationInfo innerInfo)
393399
: innerInfo(innerInfo) {}
394400

395401
/// Returns nullptr only if both this isolation info and \p other are actor
@@ -404,6 +410,8 @@ class SILDynamicMergedIsolationInfo {
404410

405411
operator bool() const { return bool(innerInfo); }
406412

413+
SILIsolationInfo *operator->() { return &innerInfo; }
414+
407415
SILIsolationInfo getIsolationInfo() const { return innerInfo; }
408416

409417
bool isDisconnected() const { return innerInfo.isDisconnected(); }
@@ -412,6 +420,12 @@ class SILDynamicMergedIsolationInfo {
412420
return innerInfo.hasSameIsolation(other);
413421
}
414422

423+
static SILDynamicMergedIsolationInfo
424+
getDisconnected(bool isUnsafeNonIsolated) {
425+
return SILDynamicMergedIsolationInfo(
426+
SILIsolationInfo::getDisconnected(isUnsafeNonIsolated));
427+
}
428+
415429
SWIFT_DEBUG_DUMP { innerInfo.dump(); }
416430

417431
void printForDiagnostics(llvm::raw_ostream &os) const {

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,14 @@ struct PartitionOpBuilder {
12051205
PartitionOp::Require(lookupValueID(value), currentInst));
12061206
}
12071207

1208+
void addRequireInOutSendingAtFunctionExit(SILValue value) {
1209+
assert(valueHasID(value, /*dumpIfHasNoID=*/true) &&
1210+
"required value should already have been encountered");
1211+
currentInstPartitionOps.emplace_back(
1212+
PartitionOp::RequireInOutSendingAtFunctionExit(lookupValueID(value),
1213+
currentInst));
1214+
}
1215+
12081216
void addUnknownPatternError(SILValue value) {
12091217
if (shouldAbortOnUnknownPatternMatchError()) {
12101218
llvm::report_fatal_error(
@@ -1604,9 +1612,9 @@ class PartitionOpTranslator {
16041612
// compiler exits successfully, actor merge errors could not have happened.
16051613
std::optional<SILDynamicMergedIsolationInfo> mergedInfo;
16061614
if (resultIsolationInfoOverride) {
1607-
mergedInfo = resultIsolationInfoOverride;
1615+
mergedInfo = SILDynamicMergedIsolationInfo(resultIsolationInfoOverride);
16081616
} else {
1609-
mergedInfo = SILIsolationInfo::getDisconnected(false);
1617+
mergedInfo = SILDynamicMergedIsolationInfo::getDisconnected(false);
16101618
}
16111619

16121620
for (SILValue src : sourceValues) {
@@ -2321,6 +2329,22 @@ class PartitionOpTranslator {
23212329
#define INST(INST, PARENT) TranslationSemantics visit##INST(INST *inst);
23222330
#include "swift/SIL/SILNodes.def"
23232331

2332+
/// Adds requires for all sending inout parameters to make sure that they are
2333+
/// properly updated before the end of the function.
2334+
void addRequiresForInOutParameters(TermInst *inst) {
2335+
assert(inst->isFunctionExiting() && "Must be function exiting term inst?!");
2336+
for (auto *arg : inst->getFunction()->getArguments()) {
2337+
auto *fArg = cast<SILFunctionArgument>(arg);
2338+
if (fArg->getArgumentConvention().isInoutConvention() &&
2339+
fArg->getKnownParameterInfo().hasOption(SILParameterInfo::Sending)) {
2340+
if (auto ns = tryToTrackValue(arg)) {
2341+
auto rep = ns->getRepresentative().getValue();
2342+
builder.addRequireInOutSendingAtFunctionExit(rep);
2343+
}
2344+
}
2345+
}
2346+
}
2347+
23242348
/// Top level switch that translates SIL instructions.
23252349
void translateSILInstruction(SILInstruction *inst) {
23262350
builder.reset(inst);
@@ -2710,12 +2734,8 @@ CONSTANT_TRANSLATION(CondFailInst, Ignored)
27102734
// function_ref/class_method which are considered sendable.
27112735
CONSTANT_TRANSLATION(SwitchValueInst, Ignored)
27122736
CONSTANT_TRANSLATION(UnreachableInst, Ignored)
2713-
CONSTANT_TRANSLATION(UnwindInst, Ignored)
2714-
// Doesn't take a parameter.
2715-
CONSTANT_TRANSLATION(ThrowAddrInst, Ignored)
27162737

27172738
// Terminators that only need require.
2718-
CONSTANT_TRANSLATION(ThrowInst, Require)
27192739
CONSTANT_TRANSLATION(SwitchEnumAddrInst, Require)
27202740
CONSTANT_TRANSLATION(YieldInst, Require)
27212741

@@ -2725,6 +2745,33 @@ CONSTANT_TRANSLATION(CondBranchInst, TerminatorPhi)
27252745
CONSTANT_TRANSLATION(CheckedCastBranchInst, TerminatorPhi)
27262746
CONSTANT_TRANSLATION(DynamicMethodBranchInst, TerminatorPhi)
27272747

2748+
// Function exiting terminators.
2749+
//
2750+
// We handle these especially since we want to make sure that inout parameters
2751+
// that are transferred are forced to be reinitialized.
2752+
//
2753+
// There is an assert in TermInst::isFunctionExiting that makes sure we do this
2754+
// correctly.
2755+
//
2756+
// NOTE: We purposely do not require reinitialization along paths that end in
2757+
// unreachable.
2758+
#ifdef FUNCTION_EXITING_TERMINATOR_TRANSLATION
2759+
#error "FUNCTION_EXITING_TERMINATOR_TRANSLATION already defined?!"
2760+
#endif
2761+
2762+
#define FUNCTION_EXITING_TERMINATOR_CONSTANT(INST, Kind) \
2763+
TranslationSemantics PartitionOpTranslator::visit##INST(INST *inst) { \
2764+
assert(inst->isFunctionExiting() && "Must be function exiting?!"); \
2765+
addRequiresForInOutParameters(inst); \
2766+
return TranslationSemantics::Kind; \
2767+
}
2768+
2769+
FUNCTION_EXITING_TERMINATOR_CONSTANT(UnwindInst, Ignored)
2770+
FUNCTION_EXITING_TERMINATOR_CONSTANT(ThrowAddrInst, Ignored)
2771+
FUNCTION_EXITING_TERMINATOR_CONSTANT(ThrowInst, Require)
2772+
2773+
#undef FUNCTION_EXITING_TERMINATOR_CONSTANT
2774+
27282775
// Today, await_async_continuation just takes Sendable values
27292776
// (UnsafeContinuation and UnsafeThrowingContinuation).
27302777
CONSTANT_TRANSLATION(AwaitAsyncContinuationInst, AssertingIfNonSendable)
@@ -2961,6 +3008,7 @@ PartitionOpTranslator::visitLoadBorrowInst(LoadBorrowInst *lbi) {
29613008
}
29623009

29633010
TranslationSemantics PartitionOpTranslator::visitReturnInst(ReturnInst *ri) {
3011+
addRequiresForInOutParameters(ri);
29643012
if (ri->getFunction()->getLoweredFunctionType()->hasSendingResult()) {
29653013
return TranslationSemantics::TransferringNoResult;
29663014
}

0 commit comments

Comments
 (0)