Skip to content

Commit e67b339

Browse files
authored
Merge pull request #71154 from gottesmm/transfernonsendable-partialapply-is-crossing
[region-isolation] Values captured by an actor isolated closure are transferred into that closure
2 parents fcda8bc + 4768d01 commit e67b339

12 files changed

+854
-285
lines changed

include/swift/AST/CaptureInfo.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@
2525

2626
namespace swift {
2727
class CapturedValue;
28-
}
28+
} // namespace swift
29+
30+
namespace swift {
31+
namespace Lowering {
32+
class TypeConverter;
33+
} // namespace Lowering
34+
} // namespace swift
2935

3036
namespace llvm {
3137
class raw_ostream;
3238
template <> struct DenseMapInfo<swift::CapturedValue>;
33-
}
39+
} // namespace llvm
3440

3541
namespace swift {
3642
class ValueDecl;
@@ -41,6 +47,8 @@ class VarDecl;
4147
/// CapturedValue includes both the declaration being captured, along with flags
4248
/// that indicate how it is captured.
4349
class CapturedValue {
50+
friend class Lowering::TypeConverter;
51+
4452
public:
4553
using Storage =
4654
llvm::PointerIntPair<llvm::PointerUnion<ValueDecl*, OpaqueValueExpr*>, 2,
@@ -69,9 +77,17 @@ class CapturedValue {
6977
CapturedValue(ValueDecl *Val, unsigned Flags, SourceLoc Loc)
7078
: Value(Val, Flags), Loc(Loc) {}
7179

72-
CapturedValue(OpaqueValueExpr *Val, unsigned Flags)
80+
private:
81+
// This is only used in TypeLowering when forming Lowered Capture
82+
// Info. OpaqueValueExpr captured value should never show up in the AST
83+
// itself.
84+
//
85+
// NOTE: AbstractClosureExpr::getIsolationCrossing relies upon this and
86+
// asserts that it never sees one of these.
87+
explicit CapturedValue(OpaqueValueExpr *Val, unsigned Flags)
7388
: Value(Val, Flags), Loc(SourceLoc()) {}
7489

90+
public:
7591
static CapturedValue getDynamicSelfMetadata() {
7692
return CapturedValue((ValueDecl *)nullptr, 0, SourceLoc());
7793
}

include/swift/AST/DiagnosticsSIL.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,14 +898,23 @@ WARNING(regionbasedisolation_transfer_yields_race_no_isolation, none,
898898
WARNING(regionbasedisolation_transfer_yields_race_with_isolation, none,
899899
"transferring value of non-Sendable type %0 from %1 context to %2 context; later accesses could race",
900900
(Type, ActorIsolation, ActorIsolation))
901+
WARNING(regionbasedisolation_isolated_capture_yields_race, none,
902+
"%1 closure captures value of non-Sendable type %0 from %2 context; later accesses to value could race",
903+
(Type, ActorIsolation, ActorIsolation))
901904
WARNING(regionbasedisolation_transfer_yields_race_transferring_parameter, none,
902905
"transferring value of non-Sendable type %0 into transferring parameter; later accesses could race",
903906
(Type))
904907
WARNING(regionbasedisolation_transfer_yields_race_stronglytransferred_binding, none,
905908
"binding of non-Sendable type %0 accessed after being transferred; later accesses could race",
906909
(Type))
910+
WARNING(regionbasedisolation_arg_transferred, none,
911+
"task isolated value of type %0 transferred to %1 context; later accesses to value could race",
912+
(Type, ActorIsolation))
907913
NOTE(regionbasedisolation_maybe_race, none,
908914
"access here could race", ())
915+
NOTE(regionbasedisolation_isolated_since_in_same_region_basename, none,
916+
"value is %0 since it is in the same region as %1",
917+
(StringRef, DeclBaseName))
909918
ERROR(regionbasedisolation_unknown_pattern, none,
910919
"pattern that the region based isolation checker does not understand how to check. Please file a bug",
911920
())

include/swift/AST/Expr.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3846,6 +3846,20 @@ class AbstractClosureExpr : public DeclContext, public Expr {
38463846
Bits.AbstractClosureExpr.Discriminator = InvalidDiscriminator;
38473847
}
38483848

3849+
/// If we find that a capture x of this AbstractClosureExpr belongs to a
3850+
/// different isolation domain than the closure, add an ApplyIsolationCrossing
3851+
/// to foundIsolationCrossing.
3852+
///
3853+
/// \p foundIsolationCrossings an out parameter that contains the
3854+
/// ApplyIsolationCrossing if any of the captures are isolation crossing and
3855+
/// the index of the capture in the capture array. We return the index since
3856+
/// all captures may not cross isolation boundaries and we may need to be able
3857+
/// to look up the corresponding capture at the SIL level by index.
3858+
void getIsolationCrossing(
3859+
SmallVectorImpl<
3860+
std::tuple<CapturedValue, unsigned, ApplyIsolationCrossing>>
3861+
&foundIsolationCrossings);
3862+
38493863
CaptureInfo getCaptureInfo() const { return Captures; }
38503864
void setCaptureInfo(CaptureInfo captures) { Captures = captures; }
38513865

lib/AST/Expr.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2927,3 +2927,55 @@ Type ThrownErrorDestination::getContextErrorType() const {
29272927
auto conversion = storage.get<Conversion *>();
29282928
return conversion->conversion->getType();
29292929
}
2930+
2931+
void AbstractClosureExpr::getIsolationCrossing(
2932+
SmallVectorImpl<std::tuple<CapturedValue, unsigned, ApplyIsolationCrossing>>
2933+
&foundIsolationCrossings) {
2934+
/// For each capture...
2935+
for (auto pair : llvm::enumerate(getCaptureInfo().getCaptures())) {
2936+
auto capture = pair.value();
2937+
2938+
// First check quickly if we have dynamic self metadata. This is Sendable
2939+
// data so we don't care about it.
2940+
if (capture.isDynamicSelfMetadata())
2941+
continue;
2942+
2943+
auto declIsolation = swift::getActorIsolation(capture.getDecl());
2944+
2945+
// Assert that we do not have an opaque value capture. These only appear in
2946+
// TypeLowering and should never appear in the AST itself.
2947+
assert(!capture.getOpaqueValue() &&
2948+
"This should only be created in TypeLowering");
2949+
2950+
// If our decl is actor isolated...
2951+
if (declIsolation.isActorIsolated()) {
2952+
// And our closure is also actor isolated, then add the apply isolation
2953+
// crossing if the actors are different.
2954+
if (actorIsolation.isActorIsolated()) {
2955+
if (declIsolation.getActor() == actorIsolation.getActor()) {
2956+
continue;
2957+
}
2958+
2959+
foundIsolationCrossings.emplace_back(
2960+
capture, pair.index(),
2961+
ApplyIsolationCrossing(declIsolation, actorIsolation));
2962+
continue;
2963+
}
2964+
2965+
// Otherwise, our closure is not actor isolated but our decl is actor
2966+
// isolated. Add it.
2967+
foundIsolationCrossings.emplace_back(
2968+
capture, pair.index(),
2969+
ApplyIsolationCrossing(declIsolation, actorIsolation));
2970+
continue;
2971+
}
2972+
2973+
if (!actorIsolation.isActorIsolated()) {
2974+
continue;
2975+
}
2976+
2977+
foundIsolationCrossings.emplace_back(
2978+
capture, pair.index(),
2979+
ApplyIsolationCrossing(declIsolation, actorIsolation));
2980+
}
2981+
}

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,15 +1574,66 @@ class PartitionOpTranslator {
15741574
}
15751575
}
15761576

1577+
/// Handles the semantics for SIL applies that cross isolation.
1578+
///
1579+
/// Semantically this causes all arguments of the applysite to be transferred.
1580+
void translateIsolatedPartialApply(PartialApplyInst *pai) {
1581+
ApplySite applySite(pai);
1582+
1583+
// For each argument operand.
1584+
for (auto &op : applySite.getArgumentOperands()) {
1585+
// See if we tracked it.
1586+
if (auto value = tryToTrackValue(op.get())) {
1587+
// If we are tracking it, transfer it and if it is actor derived, mark
1588+
// our partial apply as actor derived.
1589+
builder.addTransfer(value->getRepresentative().getValue(), &op);
1590+
}
1591+
}
1592+
1593+
// Now that we have transferred everything into the partial_apply, perform
1594+
// an assign fresh for the partial_apply. If we use any of the transferred
1595+
// values later, we will error, so it is safe to just create a new value.
1596+
auto paiValue = tryToTrackValue(pai).value();
1597+
SILValue rep = paiValue.getRepresentative().getValue();
1598+
markValueAsActorDerived(rep);
1599+
translateSILAssignFresh(rep);
1600+
}
1601+
15771602
void translateSILPartialApply(PartialApplyInst *pai) {
15781603
assert(!isIsolationBoundaryCrossingApply(pai));
15791604

15801605
// First check if our partial_apply is fed into an async let begin. If so,
15811606
// handle it especially.
1607+
//
1608+
// NOTE: If it is an async_let, then the closure itself will be Sendable. We
1609+
// treat passing in a value into the async Sendable closure as transferring
1610+
// it into the closure.
15821611
if (isAsyncLetBeginPartialApply(pai)) {
15831612
return translateSILPartialApplyAsyncLetBegin(pai);
15841613
}
15851614

1615+
// Then check if our partial apply is Sendable. In such a case, we will have
1616+
// emitted an earlier warning in Sema.
1617+
//
1618+
// DISCUSSION: The reason why we can treat values passed into an async let
1619+
// as transferring safely but it is unsafe to do this for arbitrary Sendable
1620+
// closures is that we do not know how many times the Sendable closure will
1621+
// be executed. It is possible to have different invocations of the Sendable
1622+
// closure to cause races with the captured non-Sendable value. In contrast
1623+
// since we know an async let runs exactly once, we do not need to worry
1624+
// about such a possibility. If we had the ability in the language to
1625+
// specify that a closure is run at most once or that it is always run
1626+
// serially, we could lift this restriction... so for now we leave in the
1627+
// Sema warning and just bail here.
1628+
if (pai->getFunctionType()->isSendableType())
1629+
return;
1630+
1631+
if (auto *ace = pai->getLoc().getAsASTNode<AbstractClosureExpr>()) {
1632+
if (ace->getActorIsolation().isActorIsolated()) {
1633+
return translateIsolatedPartialApply(pai);
1634+
}
1635+
}
1636+
15861637
SILMultiAssignOptions options;
15871638
for (auto arg : pai->getOperandValues()) {
15881639
if (auto value = tryToTrackValue(arg)) {

0 commit comments

Comments
 (0)