Skip to content

Commit 39d86a3

Browse files
authored
Merge pull request swiftlang#74924 from gottesmm/release/6.0-rdar126303739
[6.0][region-isolation] Add 'inout sending' diagnostics.
2 parents 70342ea + cc869f3 commit 39d86a3

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
@@ -939,6 +939,9 @@ NOTE(sil_referencebinding_inout_binding_here, none,
939939

940940
NOTE(regionbasedisolation_maybe_race, none,
941941
"access can happen concurrently", ())
942+
NOTE(regionbasedisolation_inout_sending_must_be_reinitialized, none,
943+
"'inout sending' parameter must be reinitialized before function exit with a non-actor isolated value",
944+
())
942945
ERROR(regionbasedisolation_unknown_pattern, none,
943946
"pattern that the region based isolation checker does not understand how to check. Please file a bug",
944947
())
@@ -968,6 +971,9 @@ ERROR(regionbasedisolation_arg_passed_to_strongly_transferred_param, none,
968971
NOTE(regionbasedisolation_isolated_since_in_same_region_basename, none,
969972
"value is %0 since it is in the same region as %1",
970973
(StringRef, DeclBaseName))
974+
NOTE(regionbasedisolation_isolated_since_in_same_region_string, none,
975+
"%0 is %1since it is in the same region as %2",
976+
(Identifier, StringRef, Identifier))
971977

972978
//===---
973979
// New Transfer Non Sendable Diagnostics
@@ -977,6 +983,13 @@ ERROR(regionbasedisolation_named_transfer_yields_race, none,
977983
"sending %0 risks causing data races",
978984
(Identifier))
979985

986+
ERROR(regionbasedisolation_inout_sending_cannot_be_actor_isolated, none,
987+
"'inout sending' parameter %0 cannot be %1at end of function",
988+
(Identifier, StringRef))
989+
NOTE(regionbasedisolation_inout_sending_cannot_be_actor_isolated_note, none,
990+
"%1%0 risks causing races in between %1uses and caller uses since caller assumes value is not actor isolated",
991+
(Identifier, StringRef))
992+
980993
NOTE(regionbasedisolation_named_info_transfer_yields_race, none,
981994
"sending %1%0 to %2 callee risks causing data races between %2 and local %3 uses",
982995
(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 {
@@ -1150,6 +1182,41 @@ struct PartitionOpEvaluator {
11501182
}
11511183
}
11521184
return;
1185+
case PartitionOpKind::RequireInOutSendingAtFunctionExit: {
1186+
assert(op.getOpArgs().size() == 1 &&
1187+
"Require PartitionOp should be passed 1 argument");
1188+
assert(p.isTrackingElement(op.getOpArgs()[0]) &&
1189+
"Require PartitionOp's argument should already be tracked");
1190+
1191+
// First check if the region of our 'inout sending' element has been
1192+
// transferred. In that case, we emit a special use after free error.
1193+
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[0])) {
1194+
for (auto transferredOperand : transferredOperandSet->data()) {
1195+
handleInOutSendingNotInitializedAtExitError(op, op.getOpArgs()[0],
1196+
transferredOperand);
1197+
}
1198+
return;
1199+
}
1200+
1201+
// If we were not transferred, check if our region is actor isolated. If
1202+
// so, error since we need a disconnected value in the inout parameter.
1203+
Region inoutSendingRegion = p.getRegion(op.getOpArgs()[0]);
1204+
auto dynamicRegionIsolation = getIsolationRegionInfo(inoutSendingRegion);
1205+
1206+
// If we failed to merge emit an unknown pattern error so we fail.
1207+
if (!dynamicRegionIsolation) {
1208+
handleUnknownCodePattern(op);
1209+
return;
1210+
}
1211+
1212+
// Otherwise, emit the error if the dynamic region isolation is not
1213+
// disconnected.
1214+
if (!dynamicRegionIsolation.isDisconnected()) {
1215+
handleInOutSendingNotDisconnectedAtExitError(op, op.getOpArgs()[0],
1216+
dynamicRegionIsolation);
1217+
}
1218+
return;
1219+
}
11531220
case PartitionOpKind::UnknownPatternError:
11541221
// Begin tracking the specified element in case we have a later use.
11551222
p.trackNewElement(op.getOpArgs()[0]);
@@ -1337,6 +1404,16 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
13371404
/// to our user so that we can emit that error as we process.
13381405
void handleUnknownCodePattern(const PartitionOp &op) const {}
13391406

1407+
/// Called if we find an 'inout sending' parameter that is not live at exit.
1408+
void handleInOutSendingNotInitializedAtExitError(
1409+
const PartitionOp &op, Element elt, Operand *transferringOp) const {}
1410+
1411+
/// Called if we find an 'inout sending' parameter that is live at excit but
1412+
/// is actor isolated instead of disconnected.
1413+
void handleInOutSendingNotDisconnectedAtExitError(
1414+
const PartitionOp &op, Element elt,
1415+
SILDynamicMergedIsolationInfo actorIsolation) const {}
1416+
13401417
/// This is used to determine if an element is actor derived. If we determine
13411418
/// that a region containing such an element is transferred, we emit an error
13421419
/// 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
@@ -1204,6 +1204,14 @@ struct PartitionOpBuilder {
12041204
PartitionOp::Require(lookupValueID(value), currentInst));
12051205
}
12061206

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

16101618
for (SILValue src : sourceValues) {
@@ -2304,6 +2312,22 @@ class PartitionOpTranslator {
23042312
#define INST(INST, PARENT) TranslationSemantics visit##INST(INST *inst);
23052313
#include "swift/SIL/SILNodes.def"
23062314

2315+
/// Adds requires for all sending inout parameters to make sure that they are
2316+
/// properly updated before the end of the function.
2317+
void addRequiresForInOutParameters(TermInst *inst) {
2318+
assert(inst->isFunctionExiting() && "Must be function exiting term inst?!");
2319+
for (auto *arg : inst->getFunction()->getArguments()) {
2320+
auto *fArg = cast<SILFunctionArgument>(arg);
2321+
if (fArg->getArgumentConvention().isInoutConvention() &&
2322+
fArg->getKnownParameterInfo().hasOption(SILParameterInfo::Sending)) {
2323+
if (auto ns = tryToTrackValue(arg)) {
2324+
auto rep = ns->getRepresentative().getValue();
2325+
builder.addRequireInOutSendingAtFunctionExit(rep);
2326+
}
2327+
}
2328+
}
2329+
}
2330+
23072331
/// Top level switch that translates SIL instructions.
23082332
void translateSILInstruction(SILInstruction *inst) {
23092333
builder.reset(inst);
@@ -2692,12 +2716,8 @@ CONSTANT_TRANSLATION(CondFailInst, Ignored)
26922716
// function_ref/class_method which are considered sendable.
26932717
CONSTANT_TRANSLATION(SwitchValueInst, Ignored)
26942718
CONSTANT_TRANSLATION(UnreachableInst, Ignored)
2695-
CONSTANT_TRANSLATION(UnwindInst, Ignored)
2696-
// Doesn't take a parameter.
2697-
CONSTANT_TRANSLATION(ThrowAddrInst, Ignored)
26982719

26992720
// Terminators that only need require.
2700-
CONSTANT_TRANSLATION(ThrowInst, Require)
27012721
CONSTANT_TRANSLATION(SwitchEnumAddrInst, Require)
27022722
CONSTANT_TRANSLATION(YieldInst, Require)
27032723

@@ -2707,6 +2727,33 @@ CONSTANT_TRANSLATION(CondBranchInst, TerminatorPhi)
27072727
CONSTANT_TRANSLATION(CheckedCastBranchInst, TerminatorPhi)
27082728
CONSTANT_TRANSLATION(DynamicMethodBranchInst, TerminatorPhi)
27092729

2730+
// Function exiting terminators.
2731+
//
2732+
// We handle these especially since we want to make sure that inout parameters
2733+
// that are transferred are forced to be reinitialized.
2734+
//
2735+
// There is an assert in TermInst::isFunctionExiting that makes sure we do this
2736+
// correctly.
2737+
//
2738+
// NOTE: We purposely do not require reinitialization along paths that end in
2739+
// unreachable.
2740+
#ifdef FUNCTION_EXITING_TERMINATOR_TRANSLATION
2741+
#error "FUNCTION_EXITING_TERMINATOR_TRANSLATION already defined?!"
2742+
#endif
2743+
2744+
#define FUNCTION_EXITING_TERMINATOR_CONSTANT(INST, Kind) \
2745+
TranslationSemantics PartitionOpTranslator::visit##INST(INST *inst) { \
2746+
assert(inst->isFunctionExiting() && "Must be function exiting?!"); \
2747+
addRequiresForInOutParameters(inst); \
2748+
return TranslationSemantics::Kind; \
2749+
}
2750+
2751+
FUNCTION_EXITING_TERMINATOR_CONSTANT(UnwindInst, Ignored)
2752+
FUNCTION_EXITING_TERMINATOR_CONSTANT(ThrowAddrInst, Ignored)
2753+
FUNCTION_EXITING_TERMINATOR_CONSTANT(ThrowInst, Require)
2754+
2755+
#undef FUNCTION_EXITING_TERMINATOR_CONSTANT
2756+
27102757
// Today, await_async_continuation just takes Sendable values
27112758
// (UnsafeContinuation and UnsafeThrowingContinuation).
27122759
CONSTANT_TRANSLATION(AwaitAsyncContinuationInst, AssertingIfNonSendable)
@@ -2905,6 +2952,7 @@ PartitionOpTranslator::visitLoadBorrowInst(LoadBorrowInst *lbi) {
29052952
}
29062953

29072954
TranslationSemantics PartitionOpTranslator::visitReturnInst(ReturnInst *ri) {
2955+
addRequiresForInOutParameters(ri);
29082956
if (ri->getFunction()->getLoweredFunctionType()->hasSendingResult()) {
29092957
return TranslationSemantics::TransferringNoResult;
29102958
}

0 commit comments

Comments
 (0)