Skip to content

Commit afbcf85

Browse files
committed
[region-isolation] Change named transfer non transferable error to use the dynamic merged IsolationRegionInfo found during dataflow.
I also eliminated the very basic "why is this task isolated" part of the warning in favor of the larger, better, region history impl that I am going to land soon. The diagnostic wasn't that good in the first place and also was fiddly. So I removed it for now. rdar://124960994
1 parent f7d1f2a commit afbcf85

11 files changed

+146
-131
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,7 @@ NOTE(regionbasedisolation_named_info_transfer_yields_race, none,
974974
(Identifier, ActorIsolation, ActorIsolation))
975975
NOTE(regionbasedisolation_named_transfer_non_transferrable, none,
976976
"transferring %1 %0 to %2 callee could cause races between %2 and %1 uses",
977-
(Identifier, ActorIsolation, ActorIsolation))
977+
(Identifier, StringRef, ActorIsolation))
978978
NOTE(regionbasedisolation_named_transfer_into_transferring_param, none,
979979
"%0 %1 is passed as a transferring parameter; Uses in callee may race with later %0 uses",
980980
(StringRef, Identifier))

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,15 +1090,16 @@ class ValueIsolationRegionInfo {
10901090
}
10911091

10921092
bool hasActorIsolation() const {
1093-
return std::holds_alternative<std::optional<ActorIsolation>>(data);
1093+
return kind == Actor &&
1094+
std::holds_alternative<std::optional<ActorIsolation>>(data);
10941095
}
10951096

10961097
bool hasActorInstance() const {
1097-
return std::holds_alternative<NominalTypeDecl *>(data);
1098+
return kind == Actor && std::holds_alternative<NominalTypeDecl *>(data);
10981099
}
10991100

11001101
bool hasTaskIsolatedValue() const {
1101-
return std::holds_alternative<SILValue>(data);
1102+
return kind == Task && std::holds_alternative<SILValue>(data);
11021103
}
11031104

11041105
/// If we actually have an actor decl, return that. Otherwise, see if we have
@@ -1128,8 +1129,19 @@ class ValueIsolationRegionInfo {
11281129
if (unsigned(other.kind) < unsigned(kind))
11291130
return *this;
11301131

1131-
assert(kind != ValueIsolationRegionInfo::Actor &&
1132-
"Actor should never be merged with another actor?!");
1132+
// TODO: Make this failing mean that we emit an unknown SIL error instead of
1133+
// asserting.
1134+
if (other.isActorIsolated() && isActorIsolated()) {
1135+
if (other.hasActorInstance() && hasActorInstance()) {
1136+
assert(other.getActorInstance() == getActorInstance() &&
1137+
"Actor should never be merged with another actor unless with "
1138+
"the same actor?!");
1139+
} else if (other.hasActorIsolation() && hasActorIsolation()) {
1140+
assert(other.getActorIsolation() == getActorIsolation() &&
1141+
"Actor should never be merged with another actor unless with "
1142+
"the same actor?!");
1143+
}
1144+
}
11331145

11341146
// Otherwise, take the other value.
11351147
return other;
@@ -1204,15 +1216,18 @@ struct PartitionOpEvaluator {
12041216
}
12051217

12061218
/// Call handleTransferNonTransferrable on our CRTP subclass.
1207-
void handleTransferNonTransferrable(const PartitionOp &op,
1208-
Element elt) const {
1209-
return asImpl().handleTransferNonTransferrable(op, elt);
1219+
void handleTransferNonTransferrable(
1220+
const PartitionOp &op, Element elt,
1221+
ValueIsolationRegionInfo isolationRegionInfo) const {
1222+
return asImpl().handleTransferNonTransferrable(op, elt,
1223+
isolationRegionInfo);
12101224
}
1211-
12121225
/// Just call our CRTP subclass.
1213-
void handleTransferNonTransferrable(const PartitionOp &op, Element elt,
1214-
Element otherElement) const {
1215-
return asImpl().handleTransferNonTransferrable(op, elt, otherElement);
1226+
void handleTransferNonTransferrable(
1227+
const PartitionOp &op, Element elt, Element otherElement,
1228+
ValueIsolationRegionInfo isolationRegionInfo) const {
1229+
return asImpl().handleTransferNonTransferrable(op, elt, otherElement,
1230+
isolationRegionInfo);
12161231
}
12171232

12181233
/// Call isActorDerived on our CRTP subclass.
@@ -1282,39 +1297,35 @@ struct PartitionOpEvaluator {
12821297
assert(p.isTrackingElement(op.getOpArgs()[0]) &&
12831298
"Transfer PartitionOp's argument should already be tracked");
12841299

1300+
ValueIsolationRegionInfo isolationRegionInfo =
1301+
getIsolationRegionInfo(op.getOpArgs()[0]);
1302+
12851303
// If we know our direct value is actor derived... immediately emit an
12861304
// error.
1287-
if (isActorDerived(op.getOpArgs()[0]))
1288-
return handleTransferNonTransferrable(op, op.getOpArgs()[0]);
1305+
if (isolationRegionInfo.hasActorIsolation()) {
1306+
return handleTransferNonTransferrable(op, op.getOpArgs()[0],
1307+
isolationRegionInfo);
1308+
}
12891309

1290-
// Otherwise, we may have a value that is actor derived or task isolated
1291-
// from another value. We need to prefer actor derived.
1292-
//
1293-
// While we are checking for actor derived, also check if our value or any
1294-
// value in our region is closure captured and propagate that bit in our
1295-
// transferred inst.
1310+
// Otherwise, we need to merge our isolation region info with the
1311+
// isolation region info of everything else in our region. This is the
1312+
// dynamic isolation region info found by the dataflow.
12961313
bool isClosureCapturedElt =
12971314
isClosureCaptured(op.getOpArgs()[0], op.getSourceOp());
12981315
Region elementRegion = p.getRegion(op.getOpArgs()[0]);
1299-
std::optional<Element> actorDerivedElt;
1300-
std::optional<Element> taskDerivedElt;
13011316
for (const auto &pair : p.range()) {
13021317
if (pair.second == elementRegion) {
1303-
if (isActorDerived(pair.first))
1304-
actorDerivedElt = pair.first;
1305-
if (isTaskIsolatedDerived(pair.first))
1306-
taskDerivedElt = pair.first;
1318+
isolationRegionInfo =
1319+
isolationRegionInfo.merge(getIsolationRegionInfo(pair.first));
13071320
isClosureCapturedElt |=
13081321
isClosureCaptured(pair.first, op.getSourceOp());
13091322
}
13101323
}
13111324

1312-
// Now try to add the actor derived elt first and then the task derived
1313-
// elt, preferring actor derived.
1314-
if (actorDerivedElt.has_value() || taskDerivedElt.has_value()) {
1315-
return handleTransferNonTransferrable(
1316-
op, op.getOpArgs()[0],
1317-
actorDerivedElt.has_value() ? *actorDerivedElt : *taskDerivedElt);
1325+
// If we merged anything, we need to handle a transfer non-transferrable.
1326+
if (bool(isolationRegionInfo) && !isolationRegionInfo.isDisconnected()) {
1327+
return handleTransferNonTransferrable(op, op.getOpArgs()[0],
1328+
isolationRegionInfo);
13181329
}
13191330

13201331
// Mark op.getOpArgs()[0] as transferred.
@@ -1415,14 +1426,13 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
14151426

14161427
/// This is called if we detect a never transferred element that was passed to
14171428
/// a transfer instruction.
1418-
void handleTransferNonTransferrable(const PartitionOp &op,
1419-
Element elt) const {}
1429+
void
1430+
handleTransferNonTransferrable(const PartitionOp &op, Element elt,
1431+
ValueIsolationRegionInfo regionInfo) const {}
14201432

1421-
/// This is called if we detect a never transferred element that was passed to
1422-
/// a transfer instruction but the actual element that could not be
1423-
/// transferred is a different element in its region.
1424-
void handleTransferNonTransferrable(const PartitionOp &op, Element elt,
1425-
Element otherEltInRegion) const {}
1433+
void handleTransferNonTransferrable(
1434+
const PartitionOp &op, Element elt, Element otherElement,
1435+
ValueIsolationRegionInfo isolationRegionInfo) const {}
14261436

14271437
/// This is used to determine if an element is actor derived. If we determine
14281438
/// that a region containing such an element is transferred, we emit an error

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3407,10 +3407,33 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue(
34073407
((nomDecl = self->getType().getNominalOrBoundGenericNominal()))) {
34083408
iter.first->getSecond().mergeIsolationRegionInfo(
34093409
ValueIsolationRegionInfo::getActorIsolated(nomDecl));
3410-
} else {
3411-
iter.first->getSecond().mergeIsolationRegionInfo(
3412-
ValueIsolationRegionInfo::getTaskIsolated(fArg));
3410+
return {iter.first->first, iter.first->second};
34133411
}
3412+
3413+
// See if we can infer actor isolation from our fArg's value decl.
3414+
//
3415+
// This handles functions generated by SILGen with global actors, but
3416+
// doesn't handle SIL written global actors since function arguments
3417+
// parsed from SIL do not have associated var decls.
3418+
if (auto *decl = fArg->getDecl()) {
3419+
auto isolation =
3420+
swift::getActorIsolation(const_cast<ValueDecl *>(decl));
3421+
if (!bool(isolation)) {
3422+
if (auto *dc = decl->getDeclContext()) {
3423+
isolation = swift::getActorIsolationOfContext(dc);
3424+
}
3425+
}
3426+
3427+
if (isolation.isActorIsolated()) {
3428+
iter.first->getSecond().mergeIsolationRegionInfo(
3429+
ValueIsolationRegionInfo::getActorIsolated(isolation));
3430+
return {iter.first->first, iter.first->second};
3431+
}
3432+
}
3433+
3434+
iter.first->getSecond().mergeIsolationRegionInfo(
3435+
ValueIsolationRegionInfo::getTaskIsolated(fArg));
3436+
return {iter.first->first, iter.first->second};
34143437
}
34153438
}
34163439

0 commit comments

Comments
 (0)