Skip to content

Commit 806cd79

Browse files
committed
[region-isolation] Fix actor isolated parameters to get an actor isolated error instead of a task isolated error.
Now that we actually know the region that non transferrable things belong to, we can use this information to give a better diagnostic here. A really nice effect of this is that we now emit that actor isolated parameters are actually actor isolated instead of task isolated.
1 parent a5c8106 commit 806cd79

14 files changed

+255
-88
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ class ActorIsolation {
274274
llvm_unreachable("Covered switch isn't covered?!");
275275
}
276276

277+
void printForDiagnostics(llvm::raw_ostream &os,
278+
StringRef openingQuotationMark = "'") const;
279+
277280
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
278281
};
279282

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -954,8 +954,8 @@ ERROR(regionbasedisolation_transfer_yields_race_stronglytransferred_binding, non
954954
"binding of non-Sendable type %0 accessed after being transferred; later accesses could race",
955955
(Type))
956956
ERROR(regionbasedisolation_arg_transferred, none,
957-
"task-isolated value of type %0 transferred to %1 context; later accesses to value could race",
958-
(Type, ActorIsolation))
957+
"%0 value of type %1 transferred to %2 context; later accesses to value could race",
958+
(StringRef, Type, ActorIsolation))
959959
ERROR(regionbasedisolation_arg_passed_to_strongly_transferred_param, none,
960960
"task-isolated value of type %0 passed as a strongly transferred parameter; later accesses could race",
961961
(Type))

include/swift/SILOptimizer/Analysis/RegionAnalysis.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,14 +195,16 @@ class ValueIsolationRegionInfo {
195195
}
196196
}
197197

198+
void printForDiagnostics(llvm::raw_ostream &os) const;
199+
198200
SWIFT_DEBUG_DUMP {
199201
print(llvm::dbgs());
200202
llvm::dbgs() << '\n';
201203
}
202204

203205
std::optional<ActorIsolation> getActorIsolation() const {
204206
assert(kind == Actor);
205-
assert(std::holds_alternative<NominalTypeDecl *>(data) &&
207+
assert(std::holds_alternative<std::optional<ActorIsolation>>(data) &&
206208
"Doesn't have an actor isolation?!");
207209
return std::get<std::optional<ActorIsolation>>(data);
208210
}
@@ -221,6 +223,18 @@ class ValueIsolationRegionInfo {
221223
return std::get<SILValue>(data);
222224
}
223225

226+
bool hasActorIsolation() const {
227+
return std::holds_alternative<std::optional<ActorIsolation>>(data);
228+
}
229+
230+
bool hasActorInstance() const {
231+
return std::holds_alternative<NominalTypeDecl *>(data);
232+
}
233+
234+
bool hasTaskIsolatedValue() const {
235+
return std::holds_alternative<SILValue>(data);
236+
}
237+
224238
[[nodiscard]] ValueIsolationRegionInfo
225239
merge(ValueIsolationRegionInfo other) const {
226240
// If we are greater than the other kind, then we are further along the
@@ -355,6 +369,7 @@ class regionanalysisimpl::RepresentativeValue {
355369

356370
SILValue getValue() const { return value.get<SILValue>(); }
357371
SILValue maybeGetValue() const { return value.dyn_cast<SILValue>(); }
372+
bool hasRegionIntroducingInst() const { return value.is<SILInstruction *>(); }
358373
SILInstruction *getActorRegionIntroducingInst() const {
359374
return value.get<SILInstruction *>();
360375
}
@@ -464,6 +479,10 @@ class RegionAnalysisValueMap {
464479
/// value" returns an empty SILValue.
465480
SILValue maybeGetRepresentative(Element trackableValueID) const;
466481

482+
/// Returns the fake "representative value" for this element if it
483+
/// exists. Returns nullptr otherwise.
484+
SILInstruction *maybeGetActorIntroducingInst(Element trackableValueID) const;
485+
467486
ValueIsolationRegionInfo getIsolationRegion(Element trackableValueID) const;
468487
ValueIsolationRegionInfo getIsolationRegion(SILValue trackableValueID) const;
469488

@@ -474,6 +493,15 @@ class RegionAnalysisValueMap {
474493
getTrackableValue(SILValue value,
475494
bool isAddressCapturedByPartialApply = false) const;
476495

496+
/// An actor introducing inst is an instruction that doesn't have any
497+
/// non-Sendable parameters and produces a new value that has to be actor
498+
/// isolated.
499+
///
500+
/// This is just for looking up the ValueIsolationRegionInfo for a
501+
/// instructionInst if we have one. So it is a find like function.
502+
std::optional<TrackableValue> getTrackableValueForActorIntroducingInst(
503+
SILInstruction *introducingInst) const;
504+
477505
private:
478506
std::optional<TrackableValue> getValueForId(TrackableValueID id) const;
479507
std::optional<TrackableValue> tryToTrackValue(SILValue value) const;

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,34 +1098,49 @@ struct PartitionOpEvaluator {
10981098
p.trackNewElement(op.getOpArgs()[0]);
10991099
return;
11001100
case PartitionOpKind::Transfer: {
1101+
// NOTE: We purposely do not check here if a transferred value is already
1102+
// transferred. Callers are expected to put a require for that
1103+
// purpose. This ensures that if we pass the same argument multiple times
1104+
// to the same transferring function as weakly transferred arguments, we
1105+
// do not get an error.
11011106
assert(op.getOpArgs().size() == 1 &&
11021107
"Transfer PartitionOp should be passed 1 argument");
11031108
assert(p.isTrackingElement(op.getOpArgs()[0]) &&
11041109
"Transfer PartitionOp's argument should already be tracked");
11051110

1106-
if (isActorDerived(op.getOpArgs()[0]) ||
1107-
isTaskIsolatedDerived(op.getOpArgs()[0]))
1111+
// If we know our direct value is actor derived... immediately emit an
1112+
// error.
1113+
if (isActorDerived(op.getOpArgs()[0]))
11081114
return handleTransferNonTransferrable(op, op.getOpArgs()[0]);
11091115

1110-
// NOTE: We purposely do not check here if a transferred value is already
1111-
// transferred. Callers are expected to put a require for that
1112-
// purpose. This ensures that if we pass the same argument multiple times
1113-
// to the same transferring function as weakly transferred arguments, we
1114-
// do not get an error.
1115-
1116+
// Otherwise, we may have a value that is actor derived or task isolated
1117+
// from another value. We need to prefer actor derived.
1118+
//
11161119
// While we are checking for actor derived, also check if our value or any
11171120
// value in our region is closure captured and propagate that bit in our
11181121
// transferred inst.
11191122
bool isClosureCapturedElt =
11201123
isClosureCaptured(op.getOpArgs()[0], op.getSourceOp());
1121-
11221124
Region elementRegion = p.getRegion(op.getOpArgs()[0]);
1125+
std::optional<Element> actorDerivedElt;
1126+
std::optional<Element> taskDerivedElt;
11231127
for (const auto &pair : p.range()) {
1124-
if (pair.second == elementRegion &&
1125-
(isActorDerived(pair.first) || isTaskIsolatedDerived(pair.first)))
1126-
return handleTransferNonTransferrable(op, op.getOpArgs()[0],
1127-
pair.first);
1128-
isClosureCapturedElt |= isClosureCaptured(pair.first, op.getSourceOp());
1128+
if (pair.second == elementRegion) {
1129+
if (isActorDerived(pair.first))
1130+
actorDerivedElt = pair.first;
1131+
if (isTaskIsolatedDerived(pair.first))
1132+
taskDerivedElt = pair.first;
1133+
isClosureCapturedElt |=
1134+
isClosureCaptured(pair.first, op.getSourceOp());
1135+
}
1136+
}
1137+
1138+
// Now try to add the actor derived elt first and then the task derived
1139+
// elt, preferring actor derived.
1140+
if (actorDerivedElt.has_value() || taskDerivedElt.has_value()) {
1141+
return handleTransferNonTransferrable(
1142+
op, op.getOpArgs()[0],
1143+
actorDerivedElt.has_value() ? *actorDerivedElt : *taskDerivedElt);
11291144
}
11301145

11311146
// Mark op.getOpArgs()[0] as transferred.

lib/AST/DiagnosticEngine.cpp

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -935,40 +935,12 @@ static void formatDiagnosticArgument(StringRef Modifier,
935935
Out << FormatOpts.OpeningQuotationMark << Arg.getAsLayoutConstraint()
936936
<< FormatOpts.ClosingQuotationMark;
937937
break;
938-
case DiagnosticArgumentKind::ActorIsolation:
938+
case DiagnosticArgumentKind::ActorIsolation: {
939939
assert(Modifier.empty() && "Improper modifier for ActorIsolation argument");
940-
switch (auto isolation = Arg.getAsActorIsolation()) {
941-
case ActorIsolation::ActorInstance:
942-
Out << "actor-isolated";
943-
break;
944-
945-
case ActorIsolation::GlobalActor: {
946-
if (isolation.isMainActor()) {
947-
Out << "main actor-isolated";
948-
} else {
949-
Type globalActor = isolation.getGlobalActor();
950-
Out << "global actor " << FormatOpts.OpeningQuotationMark
951-
<< globalActor.getString()
952-
<< FormatOpts.ClosingQuotationMark << "-isolated";
953-
}
954-
break;
955-
}
956-
957-
case ActorIsolation::Erased:
958-
Out << "@isolated(any)";
959-
break;
960-
961-
case ActorIsolation::Nonisolated:
962-
case ActorIsolation::NonisolatedUnsafe:
963-
case ActorIsolation::Unspecified:
964-
Out << "nonisolated";
965-
if (isolation == ActorIsolation::NonisolatedUnsafe) {
966-
Out << "(unsafe)";
967-
}
968-
break;
969-
}
940+
auto isolation = Arg.getAsActorIsolation();
941+
isolation.printForDiagnostics(Out, FormatOpts.OpeningQuotationMark);
970942
break;
971-
943+
}
972944
case DiagnosticArgumentKind::Diagnostic: {
973945
assert(Modifier.empty() && "Improper modifier for Diagnostic argument");
974946
auto diagArg = Arg.getAsDiagnostic();

lib/AST/TypeCheckRequests.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,6 +1684,38 @@ ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const {
16841684
llvm_unreachable("unhandled actor isolation kind!");
16851685
}
16861686

1687+
void ActorIsolation::printForDiagnostics(llvm::raw_ostream &os,
1688+
StringRef openingQuotationMark) const {
1689+
switch (*this) {
1690+
case ActorIsolation::ActorInstance:
1691+
os << "actor-isolated";
1692+
break;
1693+
1694+
case ActorIsolation::GlobalActor: {
1695+
if (isMainActor()) {
1696+
os << "main actor-isolated";
1697+
} else {
1698+
Type globalActor = getGlobalActor();
1699+
os << "global actor " << openingQuotationMark << globalActor.getString()
1700+
<< openingQuotationMark << "-isolated";
1701+
}
1702+
break;
1703+
}
1704+
case ActorIsolation::Erased:
1705+
os << "@isolated(any)";
1706+
break;
1707+
1708+
case ActorIsolation::Nonisolated:
1709+
case ActorIsolation::NonisolatedUnsafe:
1710+
case ActorIsolation::Unspecified:
1711+
os << "nonisolated";
1712+
if (*this == ActorIsolation::NonisolatedUnsafe) {
1713+
os << "(unsafe)";
1714+
}
1715+
break;
1716+
}
1717+
}
1718+
16871719
void swift::simple_display(
16881720
llvm::raw_ostream &out, const ActorIsolation &state) {
16891721
if (state.preconcurrency())

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,32 @@ static bool isTransferrableFunctionArgument(SILFunctionArgument *arg) {
576576
return arg->isTransferring();
577577
}
578578

579+
//===----------------------------------------------------------------------===//
580+
// MARK: ValueIsolationRegionInfo
581+
//===----------------------------------------------------------------------===//
582+
583+
void ValueIsolationRegionInfo::printForDiagnostics(
584+
llvm::raw_ostream &os) const {
585+
switch (Kind(*this)) {
586+
case Unknown:
587+
llvm::report_fatal_error("Printing unknown for diagnostics?!");
588+
return;
589+
case Disconnected:
590+
os << "disconnected";
591+
return;
592+
case Actor:
593+
if (hasActorIsolation() && getActorIsolation()) {
594+
getActorIsolation()->printForDiagnostics(os);
595+
} else {
596+
os << "actor-isolated";
597+
}
598+
return;
599+
case Task:
600+
os << "task-isolated";
601+
return;
602+
}
603+
}
604+
579605
//===----------------------------------------------------------------------===//
580606
// MARK: TrackableValue
581607
//===----------------------------------------------------------------------===//
@@ -3093,6 +3119,17 @@ void RegionAnalysisFunctionInfo::runDataflow() {
30933119
// MARK: Value Map
30943120
//===----------------------------------------------------------------------===//
30953121

3122+
SILInstruction *RegionAnalysisValueMap::maybeGetActorIntroducingInst(
3123+
Element trackableValueID) const {
3124+
if (auto value = getValueForId(trackableValueID)) {
3125+
auto rep = value->getRepresentative();
3126+
if (rep.hasRegionIntroducingInst())
3127+
return rep.getActorRegionIntroducingInst();
3128+
}
3129+
3130+
return nullptr;
3131+
}
3132+
30963133
std::optional<TrackableValue>
30973134
RegionAnalysisValueMap::getValueForId(TrackableValueID id) const {
30983135
auto iter = stateIndexToEquivalenceClass.find(id);
@@ -3281,18 +3318,40 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue(
32813318
}
32823319

32833320
// See if we have a non-transferring argument from a function. In such a case,
3284-
// mark the value as task isolated.
3321+
// mark the value as actor isolated if self is actor isolated and task
3322+
// isolated otherwise.
32853323
if (auto *fArg =
32863324
dyn_cast<SILFunctionArgument>(iter.first->first.getValue())) {
32873325
if (!isTransferrableFunctionArgument(fArg)) {
3288-
iter.first->getSecond().mergeIsolationRegionInfo(
3289-
ValueIsolationRegionInfo::getTaskIsolated(fArg));
3326+
auto *self =
3327+
iter.first->first.getValue()->getFunction()->maybeGetSelfArgument();
3328+
NominalTypeDecl *nomDecl = nullptr;
3329+
if (self &&
3330+
((nomDecl = self->getType().getNominalOrBoundGenericNominal()))) {
3331+
iter.first->getSecond().mergeIsolationRegionInfo(
3332+
ValueIsolationRegionInfo::getActorIsolated(nomDecl));
3333+
} else {
3334+
iter.first->getSecond().mergeIsolationRegionInfo(
3335+
ValueIsolationRegionInfo::getTaskIsolated(fArg));
3336+
}
32903337
}
32913338
}
32923339

32933340
return {iter.first->first, iter.first->second};
32943341
}
32953342

3343+
std::optional<TrackableValue>
3344+
RegionAnalysisValueMap::getTrackableValueForActorIntroducingInst(
3345+
SILInstruction *inst) const {
3346+
auto *self = const_cast<RegionAnalysisValueMap *>(this);
3347+
auto iter = self->equivalenceClassValuesToState.find(inst);
3348+
if (iter == self->equivalenceClassValuesToState.end())
3349+
return {};
3350+
3351+
// Otherwise, we need to compute our flags.
3352+
return {{iter->first, iter->second}};
3353+
}
3354+
32963355
std::optional<TrackableValue>
32973356
RegionAnalysisValueMap::tryToTrackValue(SILValue value) const {
32983357
auto state = getTrackableValue(value);

0 commit comments

Comments
 (0)