Skip to content

Commit 00dc538

Browse files
committed
[region-isolation] Clean up use after transfer error to use the dynamic isolation information of the transfered operand value in its diagnostic message.
As an example of the change: - // expected-note @-1 {{'x' is transferred from nonisolated caller to main actor-isolated callee. Later uses in caller could race with potential uses in callee}} + // expected-note @-1 {{transferring disconnected 'x' to main actor-isolated callee could cause races in between callee main actor-isolated and local nonisolated uses}} Part of the reason I am doing this is that I am going to be ensuring that we handle a bunch more cases and I wanted to fix this diagnostic before I added more incaranations of it to the tests.
1 parent 50b358c commit 00dc538

15 files changed

+194
-149
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -970,8 +970,8 @@ ERROR(regionbasedisolation_named_transfer_yields_race, none,
970970
(Identifier))
971971

972972
NOTE(regionbasedisolation_named_info_transfer_yields_race, none,
973-
"%0 is transferred from %1 caller to %2 callee. Later uses in caller could race with potential uses in callee",
974-
(Identifier, ActorIsolation, ActorIsolation))
973+
"transferring %1 %0 to %2 callee could cause races in between callee %2 and local %3 uses",
974+
(Identifier, StringRef, 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",
977977
(Identifier, StringRef, ActorIsolation))

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -324,58 +324,58 @@ class SILIsolationInfo {
324324

325325
namespace swift {
326326

327-
struct TransferringOperand {
327+
class TransferringOperand {
328328
using ValueType = llvm::PointerIntPair<Operand *, 1>;
329329
ValueType value;
330330

331-
TransferringOperand() : value() {}
332-
TransferringOperand(Operand *op, bool isClosureCaptured)
333-
: value(op, isClosureCaptured) {}
334-
explicit TransferringOperand(Operand *op) : value(op, false) {}
335-
TransferringOperand(ValueType newValue) : value(newValue) {}
331+
/// The dynamic isolation info of the region of value when we transferred.
332+
SILIsolationInfo isolationInfo;
333+
334+
TransferringOperand(ValueType newValue, SILIsolationInfo isolationRegionInfo)
335+
: value(newValue), isolationInfo(isolationRegionInfo) {
336+
assert(isolationInfo && "Should never see unknown isolation info");
337+
}
338+
339+
public:
340+
TransferringOperand(Operand *op, bool isClosureCaptured,
341+
SILIsolationInfo isolationRegionInfo)
342+
: TransferringOperand({op, isClosureCaptured}, isolationRegionInfo) {}
343+
explicit TransferringOperand(Operand *op,
344+
SILIsolationInfo isolationRegionInfo)
345+
: TransferringOperand({op, false}, isolationRegionInfo) {}
336346

337347
operator bool() const { return bool(value.getPointer()); }
338348

339349
Operand *getOperand() const { return value.getPointer(); }
340350

351+
SILValue get() const { return getOperand()->get(); }
352+
341353
bool isClosureCaptured() const { return value.getInt(); }
342354

343355
SILInstruction *getUser() const { return getOperand()->getUser(); }
344356

345-
bool operator<(const TransferringOperand &other) const {
346-
return value < other.value;
347-
}
357+
SILIsolationInfo getIsolationInfo() const { return isolationInfo; }
348358

349-
bool operator>=(const TransferringOperand &other) const {
350-
return !(value < other.value);
351-
}
352-
353-
bool operator>(const TransferringOperand &other) const {
354-
return value > other.value;
355-
}
356-
357-
bool operator<=(const TransferringOperand &other) const {
358-
return !(value > other.value);
359-
}
360-
361-
bool operator==(const TransferringOperand &other) const {
362-
return value == other.value;
363-
}
359+
unsigned getOperandNumber() const { return getOperand()->getOperandNumber(); }
364360

365361
void print(llvm::raw_ostream &os) const {
366362
os << "Op Num: " << getOperand()->getOperandNumber() << ". "
367363
<< "Capture: " << (isClosureCaptured() ? "yes. " : "no. ")
368-
<< "User: " << *getUser();
364+
<< "IsolationInfo: ";
365+
isolationInfo.print(os);
366+
os << "\nUser: " << *getUser();
369367
}
370368

371369
static void Profile(llvm::FoldingSetNodeID &id, Operand *op,
372-
bool isClosureCaptured) {
370+
bool isClosureCaptured,
371+
SILIsolationInfo isolationRegionInfo) {
373372
id.AddPointer(op);
374373
id.AddBoolean(isClosureCaptured);
374+
isolationRegionInfo.Profile(id);
375375
}
376376

377377
void Profile(llvm::FoldingSetNodeID &id) const {
378-
Profile(id, getOperand(), isClosureCaptured());
378+
Profile(id, getOperand(), isClosureCaptured(), isolationInfo);
379379
}
380380

381381
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
@@ -1367,8 +1367,8 @@ struct PartitionOpEvaluator {
13671367
}
13681368

13691369
// Mark op.getOpArgs()[0] as transferred.
1370-
auto *ptrSet =
1371-
ptrSetFactory.emplace(op.getSourceOp(), isClosureCapturedElt);
1370+
auto *ptrSet = ptrSetFactory.emplace(
1371+
op.getSourceOp(), isClosureCapturedElt, transferredRegionIsolation);
13721372
p.markTransferred(op.getOpArgs()[0], ptrSet);
13731373
return;
13741374
}

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2967,6 +2967,10 @@ bool BlockPartitionState::recomputeExitFromEntry(
29672967
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory),
29682968
translator(translator) {}
29692969

2970+
SILIsolationInfo getIsolationRegionInfo(Element elt) const {
2971+
return translator.getValueMap().getIsolationRegion(elt);
2972+
}
2973+
29702974
bool isClosureCaptured(Element elt, Operand *op) const {
29712975
auto iter = translator.getValueForId(elt);
29722976
if (!iter)

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -421,9 +421,34 @@ struct TransferredNonTransferrableInfo {
421421
isolationRegionInfo(isolationRegionInfo) {}
422422
};
423423

424+
/// A pointer to a TransferringOperand pointer that sorts according to the
425+
/// Operand pointer in the TransferringOperand rather than the pointer of the
426+
/// TransferringOperand itself.
427+
class TransferringOperandRef {
428+
TransferringOperand *operand;
429+
430+
public:
431+
TransferringOperandRef(TransferringOperand *operand) : operand(operand) {}
432+
433+
TransferringOperand *operator*() const { return operand; }
434+
TransferringOperand *operator->() const { return operand; }
435+
436+
bool operator==(const TransferringOperandRef &other) const {
437+
return operand->getOperand() == other->getOperand();
438+
}
439+
440+
bool operator!=(const TransferringOperandRef &other) const {
441+
return !(*this == other);
442+
}
443+
444+
bool operator<(const TransferringOperandRef &other) const {
445+
return operand->getOperand() < other->getOperand();
446+
}
447+
};
448+
424449
class TransferNonSendableImpl {
425450
RegionAnalysisFunctionInfo *regionInfo;
426-
SmallFrozenMultiMap<Operand *, SILInstruction *, 8>
451+
SmallFrozenMultiMap<TransferringOperandRef, SILInstruction *, 8>
427452
transferOpToRequireInstMultiMap;
428453
SmallVector<TransferredNonTransferrableInfo, 8>
429454
transferredNonTransferrableInfoList;
@@ -467,6 +492,7 @@ class UseAfterTransferDiagnosticEmitter {
467492
}
468493

469494
void emitNamedIsolationCrossingError(SILLocation loc, Identifier name,
495+
SILIsolationInfo namesIsolationInfo,
470496
ApplyIsolationCrossing isolationCrossing,
471497
SILLocation variableDefinedLoc) {
472498
// Emit the short error.
@@ -475,10 +501,15 @@ class UseAfterTransferDiagnosticEmitter {
475501
.highlight(loc.getSourceRange());
476502

477503
// Then emit the note with greater context.
478-
diagnoseNote(loc,
479-
diag::regionbasedisolation_named_info_transfer_yields_race,
480-
name, isolationCrossing.getCallerIsolation(),
481-
isolationCrossing.getCalleeIsolation());
504+
SmallString<64> descriptiveKindStr;
505+
{
506+
llvm::raw_svector_ostream os(descriptiveKindStr);
507+
namesIsolationInfo.printForDiagnostics(os);
508+
}
509+
diagnoseNote(
510+
loc, diag::regionbasedisolation_named_info_transfer_yields_race, name,
511+
descriptiveKindStr, isolationCrossing.getCalleeIsolation(),
512+
isolationCrossing.getCallerIsolation());
482513
emitRequireInstDiagnostics();
483514
}
484515

@@ -492,7 +523,7 @@ class UseAfterTransferDiagnosticEmitter {
492523
emitRequireInstDiagnostics();
493524
}
494525

495-
void emitUseOfStringlyTransferredValue(SILLocation loc, Type inferredType) {
526+
void emitUseOfStronglyTransferredValue(SILLocation loc, Type inferredType) {
496527
diagnoseError(
497528
loc,
498529
diag::
@@ -580,7 +611,7 @@ class UseAfterTransferDiagnosticEmitter {
580611
};
581612

582613
class UseAfterTransferDiagnosticInferrer {
583-
Operand *transferOp;
614+
TransferringOperand *transferOp;
584615
UseAfterTransferDiagnosticEmitter diagnosticEmitter;
585616
RegionAnalysisValueMap &valueMap;
586617
SILLocation baseLoc = SILLocation::invalid();
@@ -590,13 +621,18 @@ class UseAfterTransferDiagnosticInferrer {
590621

591622
public:
592623
UseAfterTransferDiagnosticInferrer(
593-
Operand *transferOp, SmallVectorImpl<SILInstruction *> &requireInsts,
624+
TransferringOperand *transferOp,
625+
SmallVectorImpl<SILInstruction *> &requireInsts,
594626
RegionAnalysisValueMap &valueMap)
595-
: transferOp(transferOp), diagnosticEmitter(transferOp, requireInsts),
627+
: transferOp(transferOp),
628+
diagnosticEmitter(transferOp->getOperand(), requireInsts),
596629
valueMap(valueMap), baseLoc(transferOp->getUser()->getLoc()),
597-
baseInferredType(transferOp->get()->getType().getASTType()) {}
630+
baseInferredType(
631+
transferOp->getOperand()->get()->getType().getASTType()) {}
598632
void infer();
599633

634+
Operand *getTransferringOperand() const { return transferOp->getOperand(); }
635+
600636
private:
601637
bool initForIsolatedPartialApply(Operand *op, AbstractClosureExpr *ace);
602638

@@ -742,11 +778,12 @@ struct UseAfterTransferDiagnosticInferrer::Walker : ASTWalker {
742778
void UseAfterTransferDiagnosticInferrer::infer() {
743779
// Otherwise, see if our operand's instruction is a transferring parameter.
744780
if (auto fas = FullApplySite::isa(transferOp->getUser())) {
745-
assert(!fas.getArgumentConvention(*transferOp).isIndirectOutParameter() &&
781+
assert(!fas.getArgumentConvention(*transferOp->getOperand())
782+
.isIndirectOutParameter() &&
746783
"We should never transfer an indirect out parameter");
747-
if (fas.getArgumentParameterInfo(*transferOp)
784+
if (fas.getArgumentParameterInfo(*transferOp->getOperand())
748785
.hasOption(SILParameterInfo::Transferring)) {
749-
return diagnosticEmitter.emitUseOfStringlyTransferredValue(
786+
return diagnosticEmitter.emitUseOfStronglyTransferredValue(
750787
baseLoc, baseInferredType);
751788
}
752789
}
@@ -757,7 +794,7 @@ void UseAfterTransferDiagnosticInferrer::infer() {
757794
// transfer error due to us transferring a value into it.
758795
if (auto *ace = loc.getAsASTNode<AbstractClosureExpr>()) {
759796
if (ace->getActorIsolation().isActorIsolated()) {
760-
if (initForIsolatedPartialApply(transferOp, ace)) {
797+
if (initForIsolatedPartialApply(transferOp->getOperand(), ace)) {
761798
return;
762799
}
763800
}
@@ -770,21 +807,21 @@ void UseAfterTransferDiagnosticInferrer::infer() {
770807
if (auto *svi =
771808
dyn_cast<SingleValueInstruction>(rootValueAndName->second)) {
772809
return diagnosticEmitter.emitNamedIsolationCrossingError(
773-
baseLoc, rootValueAndName->first,
810+
baseLoc, rootValueAndName->first, transferOp->getIsolationInfo(),
774811
*sourceApply->getIsolationCrossing(), svi->getLoc());
775812
}
776813

777814
if (auto *fArg =
778815
dyn_cast<SILFunctionArgument>(rootValueAndName->second)) {
779816
return diagnosticEmitter.emitNamedIsolationCrossingError(
780-
baseLoc, rootValueAndName->first,
817+
baseLoc, rootValueAndName->first, transferOp->getIsolationInfo(),
781818
*sourceApply->getIsolationCrossing(),
782819
RegularLocation(fArg->getDecl()->getLoc()));
783820
}
784821
}
785822

786823
// Otherwise, try to infer from the ApplyExpr.
787-
return initForApply(transferOp, sourceApply);
824+
return initForApply(transferOp->getOperand(), sourceApply);
788825
}
789826

790827
if (auto fas = FullApplySite::isa(transferOp->getUser())) {
@@ -801,7 +838,7 @@ void UseAfterTransferDiagnosticInferrer::infer() {
801838

802839
auto *i = transferOp->getUser();
803840
auto pai = ApplySite::isa(i);
804-
unsigned captureIndex = pai.getAppliedArgIndex(*transferOp);
841+
unsigned captureIndex = pai.getAppliedArgIndex(*transferOp->getOperand());
805842

806843
auto captureInfo =
807844
autoClosureExpr->getCaptureInfo().getCaptures()[captureIndex];
@@ -823,8 +860,9 @@ void TransferNonSendableImpl::emitUseAfterTransferDiagnostics() {
823860

824861
LLVM_DEBUG(llvm::dbgs() << "Emitting use after transfer diagnostics.\n");
825862

826-
for (auto [transferOp, requireInsts] :
863+
for (auto [transferOpRef, requireInsts] :
827864
transferOpToRequireInstMultiMap.getRange()) {
865+
auto *transferOp = *transferOpRef;
828866

829867
LLVM_DEBUG(llvm::dbgs()
830868
<< "Transfer Op. Number: " << transferOp->getOperandNumber()
@@ -834,8 +872,8 @@ void TransferNonSendableImpl::emitUseAfterTransferDiagnostics() {
834872
// single we don't understand error if we do not find the require.
835873
bool didEmitRequireNote = false;
836874
InstructionSet requireInstsUnique(function);
837-
RequireLiveness liveness(blockLivenessInfoGeneration, transferOp,
838-
blockLivenessInfo);
875+
RequireLiveness liveness(blockLivenessInfoGeneration,
876+
transferOp->getOperand(), blockLivenessInfo);
839877
++blockLivenessInfoGeneration;
840878
liveness.process(requireInsts);
841879

@@ -860,7 +898,8 @@ void TransferNonSendableImpl::emitUseAfterTransferDiagnostics() {
860898
// tells the user to file a bug. This importantly ensures that we can
861899
// guarantee that we always find the require if we successfully compile.
862900
if (!didEmitRequireNote) {
863-
diagnoseError(transferOp, diag::regionbasedisolation_unknown_pattern);
901+
diagnoseError(transferOp->getOperand(),
902+
diag::regionbasedisolation_unknown_pattern);
864903
continue;
865904
}
866905

@@ -1213,20 +1252,20 @@ namespace {
12131252
struct DiagnosticEvaluator final
12141253
: PartitionOpEvaluatorBaseImpl<DiagnosticEvaluator> {
12151254
RegionAnalysisFunctionInfo *info;
1216-
SmallFrozenMultiMap<Operand *, SILInstruction *, 8>
1255+
SmallFrozenMultiMap<TransferringOperandRef, SILInstruction *, 8>
12171256
&transferOpToRequireInstMultiMap;
12181257

12191258
/// First value is the operand that was transferred... second value is the
12201259
/// non-transferrable value in the same region as that value. The second value
12211260
/// is what is non-transferrable.
12221261
SmallVectorImpl<TransferredNonTransferrableInfo> &transferredNonTransferrable;
12231262

1224-
DiagnosticEvaluator(Partition &workingPartition,
1225-
RegionAnalysisFunctionInfo *info,
1226-
SmallFrozenMultiMap<Operand *, SILInstruction *, 8>
1227-
&transferOpToRequireInstMultiMap,
1228-
SmallVectorImpl<TransferredNonTransferrableInfo>
1229-
&transferredNonTransferrable)
1263+
DiagnosticEvaluator(
1264+
Partition &workingPartition, RegionAnalysisFunctionInfo *info,
1265+
SmallFrozenMultiMap<TransferringOperandRef, SILInstruction *, 8>
1266+
&transferOpToRequireInstMultiMap,
1267+
SmallVectorImpl<TransferredNonTransferrableInfo>
1268+
&transferredNonTransferrable)
12301269
: PartitionOpEvaluatorBaseImpl(workingPartition,
12311270
info->getOperandSetFactory()),
12321271
info(info),
@@ -1261,7 +1300,7 @@ struct DiagnosticEvaluator final
12611300
<< " ID: %%" << transferredVal << "\n"
12621301
<< " Rep: " << *rep << " Transferring Op Num: "
12631302
<< transferringOp->getOperand()->getOperandNumber() << '\n');
1264-
transferOpToRequireInstMultiMap.insert(transferringOp->getOperand(),
1303+
transferOpToRequireInstMultiMap.insert({transferringOp},
12651304
partitionOp.getSourceInst());
12661305
}
12671306

test/Concurrency/experimental_feature_strictconcurrency.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func iterate(stream: AsyncStream<Int>) async {
3232
// declaration to be in a disconnected region
3333

3434
// expected-region-isolation-warning @+3 {{transferring 'it' may cause a race}}
35-
// expected-region-isolation-note @+2 {{'it' is transferred from main actor-isolated caller to nonisolated callee. Later uses in caller could race with potential uses in callee}}
35+
// expected-region-isolation-note @+2 {{transferring disconnected 'it' to nonisolated callee could cause races in between callee nonisolated and local main actor-isolated uses}}
3636
// expected-region-isolation-note @+1 {{access here could race}}
3737
while let element = await it.next() {
3838
print(element)

test/Concurrency/sendable_checking.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ func testNonSendableBaseArg() async {
278278
await t.update()
279279
// expected-targeted-and-complete-warning @-1 {{passing argument of non-sendable type 'NonSendable' into main actor-isolated context may introduce data races}}
280280
// expected-tns-warning @-2 {{transferring 't' may cause a race}}
281-
// expected-tns-note @-3 {{'t' is transferred from nonisolated caller to main actor-isolated callee. Later uses in caller could race with potential uses in callee}}
281+
// expected-tns-note @-3 {{transferring disconnected 't' to main actor-isolated callee could cause races in between callee main actor-isolated and local nonisolated uses}}
282282

283283
_ = await t.x
284284
// expected-warning @-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to main actor-isolated property 'x' cannot cross actor boundary}}

0 commit comments

Comments
 (0)