Skip to content

Commit 23b6937

Browse files
committed
[rbi] Make it so that we correctly do not error on uses of Sendable values that are projected from non-Sendable bases.
Specifically, we only do this if the base is a let or if it is a var but not captured by reference. rdar://149019222
1 parent a045c98 commit 23b6937

File tree

6 files changed

+101
-23
lines changed

6 files changed

+101
-23
lines changed

include/swift/SILOptimizer/Analysis/RegionAnalysis.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,12 @@ class RegionAnalysisValueMap {
368368
}
369369

370370
operator bool() const { return value; }
371+
372+
void print(llvm::raw_ostream &os) const;
373+
SWIFT_DEBUG_DUMP {
374+
print(llvm::dbgs());
375+
llvm::dbgs() << '\n';
376+
}
371377
};
372378

373379
/// A map from a SILValue to its equivalence class representative.

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,13 +1461,6 @@ struct PartitionOpEvaluator {
14611461
return;
14621462
}
14631463

1464-
// If this is a require of a mutable base of a Sendable value and we were
1465-
// not closure captured, we can bail.
1466-
if (op.getOptions().containsOnly(PartitionOp::Flag::RequireOfMutableBaseOfSendableValue) &&
1467-
regionHasClosureCapturedElt) {
1468-
return;
1469-
}
1470-
14711464
// Mark op.getOpArg1() as sent.
14721465
SendingOperandState &state = operandToStateMap.get(op.getSourceOp());
14731466
state.isClosureCaptured |= regionHasClosureCapturedElt;

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ static bool isStaticallyLookThroughInst(SILInstruction *inst) {
310310
case SILInstructionKind::UnmanagedToRefInst:
311311
case SILInstructionKind::InitExistentialValueInst:
312312
case SILInstructionKind::UncheckedEnumDataInst:
313+
case SILInstructionKind::StructElementAddrInst:
314+
case SILInstructionKind::TupleElementAddrInst:
313315
return true;
314316
case SILInstructionKind::MoveValueInst:
315317
// Look through if it isn't from a var decl.
@@ -334,9 +336,6 @@ static bool isLookThroughIfOperandAndResultNonSendable(SILInstruction *inst) {
334336
case SILInstructionKind::UncheckedTrivialBitCastInst:
335337
case SILInstructionKind::UncheckedBitwiseCastInst:
336338
case SILInstructionKind::UncheckedValueCastInst:
337-
case SILInstructionKind::StructElementAddrInst:
338-
case SILInstructionKind::TupleElementAddrInst:
339-
case SILInstructionKind::UncheckedTakeEnumDataAddrInst:
340339
case SILInstructionKind::ConvertEscapeToNoEscapeInst:
341340
case SILInstructionKind::ConvertFunctionInst:
342341
case SILInstructionKind::RefToRawPointerInst:
@@ -735,6 +734,7 @@ TrackableValueLookupResult RegionAnalysisValueMap::getTrackableValue(
735734

736735
auto trackedValue =
737736
getTrackableValueHelper(value, isAddressCapturedByPartialApply);
737+
738738
std::optional<TrackableValue> trackedBase;
739739
if (base)
740740
trackedBase =
@@ -1130,6 +1130,17 @@ void TrackableValueLookupResult::print(llvm::raw_ostream &os) const {
11301130
}
11311131
}
11321132

1133+
//===----------------------------------------------------------------------===//
1134+
// MARK: UnderlyingTrackedValueInfo
1135+
//===----------------------------------------------------------------------===//
1136+
1137+
void RegionAnalysisValueMap::UnderlyingTrackedValueInfo::print(
1138+
llvm::raw_ostream &os) const {
1139+
os << "RegionAnalysisValueMap.\nValue: " << value;
1140+
if (base)
1141+
os << "Base: " << base;
1142+
}
1143+
11331144
//===----------------------------------------------------------------------===//
11341145
// MARK: Partial Apply Reachability
11351146
//===----------------------------------------------------------------------===//
@@ -3204,10 +3215,16 @@ void PartitionOpBuilder::addRequire(TrackableValueLookupResult value) {
32043215
if (!boxType->getLayout()->isMutable())
32053216
return;
32063217

3218+
options |= PartitionOp::Flag::RequireOfMutableBaseOfSendableValue;
3219+
} else if (auto *asi = dyn_cast<AllocStackInst>(rep)) {
3220+
if (asi->isLet())
3221+
return;
3222+
32073223
options |= PartitionOp::Flag::RequireOfMutableBaseOfSendableValue;
32083224
}
3225+
3226+
addRequire(value.base, options);
32093227
}
3210-
addRequire(value.base, options);
32113228
}
32123229
}
32133230

@@ -3355,6 +3372,9 @@ CONSTANT_TRANSLATION(RefToUnmanagedInst, LookThrough)
33553372
CONSTANT_TRANSLATION(UnmanagedToRefInst, LookThrough)
33563373
CONSTANT_TRANSLATION(InitExistentialValueInst, LookThrough)
33573374
CONSTANT_TRANSLATION(UncheckedEnumDataInst, LookThrough)
3375+
CONSTANT_TRANSLATION(TupleElementAddrInst, LookThrough)
3376+
CONSTANT_TRANSLATION(StructElementAddrInst, LookThrough)
3377+
CONSTANT_TRANSLATION(UncheckedTakeEnumDataAddrInst, LookThrough)
33583378

33593379
//===---
33603380
// Store
@@ -3604,9 +3624,6 @@ IGNORE_IF_SENDABLE_RESULT_ASSIGN_OTHERWISE(StructExtractInst)
36043624
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedTrivialBitCastInst)
36053625
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedBitwiseCastInst)
36063626
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedValueCastInst)
3607-
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(TupleElementAddrInst)
3608-
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(StructElementAddrInst)
3609-
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedTakeEnumDataAddrInst)
36103627
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(ConvertEscapeToNoEscapeInst)
36113628
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(ConvertFunctionInst)
36123629

@@ -3701,20 +3718,42 @@ PartitionOpTranslator::visitBeginBorrowInst(BeginBorrowInst *bbi) {
37013718
return TranslationSemantics::LookThrough;
37023719
}
37033720

3704-
/// LoadInst is technically a statically look through instruction, but we want
3705-
/// to handle it especially in the infrastructure, so we cannot mark it as
3706-
/// such. This makes marking it as a normal lookthrough instruction impossible
3707-
/// since the routine checks that invariant.
3708-
TranslationSemantics PartitionOpTranslator::visitLoadInst(LoadInst *limvi) {
3721+
/// LoadInst has two different semantics:
3722+
///
3723+
/// 1. If the load produces a non-Sendable value, we want to treat it as a
3724+
/// statically look through instruction, but we want to handle it especially in
3725+
/// the infrastructure, so we cannot mark it as such. This makes marking it as a
3726+
/// normal lookthrough instruction impossible since the routine checks that
3727+
/// invariant.
3728+
///
3729+
/// 2. If the load produces a Sendable value, we want to perform a require on
3730+
/// the address so that if we have a load from a non-Sendable base, we properly
3731+
/// require the base.
3732+
TranslationSemantics PartitionOpTranslator::visitLoadInst(LoadInst *li) {
3733+
if (SILIsolationInfo::isSendableType(li->getOperand())) {
3734+
translateSILRequire(li->getOperand());
3735+
}
3736+
37093737
return TranslationSemantics::Special;
37103738
}
37113739

3712-
/// LoadBorrowInst is technically a statically look through instruction, but we
3713-
/// want to handle it especially in the infrastructure, so we cannot mark it as
3714-
/// such. This makes marking it as a normal lookthrough instruction impossible
3715-
/// since the routine checks that invariant.
3740+
/// LoadBorrowInst has two different semantics:
3741+
///
3742+
/// 1. If the load produces a non-Sendable value, we want to treat it as a
3743+
/// statically look through instruction, but we want to handle it especially in
3744+
/// the infrastructure, so we cannot mark it as such. This makes marking it as a
3745+
/// normal lookthrough instruction impossible since the routine checks that
3746+
/// invariant.
3747+
///
3748+
/// 2. If the load produces a Sendable value, we want to perform a require on
3749+
/// the address so that if we have a load from a non-Sendable base, we properly
3750+
/// require the base.
37163751
TranslationSemantics
37173752
PartitionOpTranslator::visitLoadBorrowInst(LoadBorrowInst *lbi) {
3753+
if (SILIsolationInfo::isSendableType(lbi->getOperand())) {
3754+
translateSILRequire(lbi->getOperand());
3755+
}
3756+
37183757
return TranslationSemantics::Special;
37193758
}
37203759

lib/SILOptimizer/Mandatory/SendNonSendable.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2702,6 +2702,14 @@ struct DiagnosticEvaluator final
27022702
void handleLocalUseAfterSend(LocalUseAfterSendError error) const {
27032703
const auto &partitionOp = *error.op;
27042704
REGIONBASEDISOLATION_LOG(error.print(llvm::dbgs(), info->getValueMap()));
2705+
2706+
// Ignore this if we are erroring on a mutable base of a Sendable value and
2707+
// if when we sent the value's region was not closure captured.
2708+
if (error.op->getOptions().containsOnly(
2709+
PartitionOp::Flag::RequireOfMutableBaseOfSendableValue) &&
2710+
!operandToStateMap.get(error.sendingOp).isClosureCaptured)
2711+
return;
2712+
27052713
sendingOpToRequireInstMultiMap.insert(
27062714
error.sendingOp, RequireInst::forUseAfterSend(partitionOp.getSourceInst()));
27072715
}

lib/SILOptimizer/Utils/PartitionUtils.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const {
142142
}
143143
case PartitionOpKind::Require: {
144144
os << "require ";
145+
if (getOptions().containsOnly(
146+
PartitionOp::Flag::RequireOfMutableBaseOfSendableValue))
147+
os << "[mutable_base_of_sendable_val] ";
145148
if (extraSpace)
146149
os << extraSpaceLiteral;
147150
os << "%%" << getOpArg1();

test/Concurrency/transfernonsendable.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,3 +1968,32 @@ extension NonIsolatedFinalKlass {
19681968
// expected-tns-note @-1 {{sending task-isolated 'self.ns' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and task-isolated uses}}
19691969
}
19701970
}
1971+
1972+
func mutableLocalCaptureDataRace() async {
1973+
var x = 0
1974+
x = 0
1975+
_ = x
1976+
1977+
Task.detached { x = 1 } // expected-tns-warning {{sending value of non-Sendable type '() async -> ()' risks causing data races}}
1978+
// expected-tns-note @-1 {{Passing value of non-Sendable type '() async -> ()' as a 'sending' argument to static method 'detached(priority:operation:)' risks causing races in between local and caller code}}
1979+
1980+
x = 2 // expected-tns-note {{access can happen concurrently}}
1981+
}
1982+
1983+
func mutableLocalCaptureDataRace2() async {
1984+
var x = 0
1985+
x = 0
1986+
1987+
Task.detached { x = 1 } // expected-tns-warning {{sending value of non-Sendable type '() async -> ()' risks causing data races}}
1988+
// expected-tns-note @-1 {{Passing value of non-Sendable type '() async -> ()' as a 'sending' argument to static method 'detached(priority:operation:)' risks causing races in between local and caller code}}
1989+
1990+
print(x) // expected-tns-note {{access can happen concurrently}}
1991+
}
1992+
1993+
func localCaptureDataRace3() async {
1994+
let x = 0
1995+
1996+
Task.detached { print(x) }
1997+
1998+
print(x)
1999+
}

0 commit comments

Comments
 (0)