Skip to content

Commit 02be75a

Browse files
committed
[region-isolation] Emit a better error when a function parameter is assigned into a different transferring parameter.
Just eliminating another "call site passes `self`" error.
1 parent 8fd70ec commit 02be75a

File tree

4 files changed

+119
-6
lines changed

4 files changed

+119
-6
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,13 @@ NOTE(regionbasedisolation_isolated_since_in_same_region_basename, none,
973973
ERROR(regionbasedisolation_named_transfer_yields_race, none,
974974
"transferring %0 may cause a race",
975975
(Identifier))
976+
ERROR(regionbasedisolation_stronglytransfer_assignment_yields_race_name, none,
977+
"assigning %0 to transferring parameter %1 may cause a race",
978+
(Identifier, Identifier))
979+
NOTE(regionbasedisolation_stronglytransfer_taskisolated_assign_note, none,
980+
"%0 is a task isolated value that is assigned into transferring parameter %1. Transferred uses of %1 may race with caller uses of %0",
981+
(Identifier, Identifier))
982+
976983
NOTE(regionbasedisolation_named_info_transfer_yields_race, none,
977984
"%0 is transferred from %1 caller to %2 callee. Later uses in caller could race with potential uses in callee",
978985
(Identifier, ActorIsolation, ActorIsolation))

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -995,12 +995,17 @@ class TransferNonTransferrableDiagnosticInferrer {
995995

996996
/// Used if we have a named variable.
997997
NamedIsolation = 5,
998+
999+
/// Used if a non-transferring function argument is assigned to a
1000+
/// transferring function argument.
1001+
FunctionArgumentAssignedToStronglyTransferredParam = 6,
9981002
};
9991003

10001004
class UseDiagnosticInfo {
10011005
UseDiagnosticInfoKind kind;
10021006
std::optional<ApplyIsolationCrossing> transferredIsolationCrossing = {};
10031007
llvm::PointerUnion<Type, Identifier> inferredTypeOrIdentifier = {};
1008+
std::optional<Identifier> transferredParamIdentifier = {};
10041009

10051010
public:
10061011
UseDiagnosticInfoKind getKind() const { return kind; }
@@ -1010,6 +1015,9 @@ class TransferNonTransferrableDiagnosticInferrer {
10101015
Identifier getName() const {
10111016
return inferredTypeOrIdentifier.get<Identifier>();
10121017
}
1018+
Identifier getTransferredParamIdentifier() const {
1019+
return transferredParamIdentifier.value();
1020+
}
10131021
Type getType() const { return inferredTypeOrIdentifier.get<Type>(); }
10141022

10151023
static UseDiagnosticInfo forMiscUse() {
@@ -1038,24 +1046,37 @@ class TransferNonTransferrableDiagnosticInferrer {
10381046
return {UseDiagnosticInfoKind::FunctionArgumentApplyStronglyTransferred};
10391047
}
10401048

1049+
static UseDiagnosticInfo
1050+
forFunctionArgumentAssignedToStronglyTransferredParam(
1051+
Identifier funcArgName, Identifier transferredParam) {
1052+
return {UseDiagnosticInfoKind::
1053+
FunctionArgumentAssignedToStronglyTransferredParam,
1054+
{},
1055+
funcArgName,
1056+
transferredParam};
1057+
}
1058+
10411059
private:
10421060
UseDiagnosticInfo(
10431061
UseDiagnosticInfoKind kind,
10441062
std::optional<ApplyIsolationCrossing> isolation = {},
1045-
llvm::PointerUnion<Type, Identifier> inferredTypeOrIdentifier = {})
1063+
llvm::PointerUnion<Type, Identifier> inferredTypeOrIdentifier = {},
1064+
std::optional<Identifier> transferredParamIdentifier = {})
10461065
: kind(kind), transferredIsolationCrossing(isolation),
1047-
inferredTypeOrIdentifier(inferredTypeOrIdentifier) {}
1066+
inferredTypeOrIdentifier(inferredTypeOrIdentifier),
1067+
transferredParamIdentifier(transferredParamIdentifier) {}
10481068
};
10491069

10501070
private:
1071+
RegionAnalysisValueMap &valueMap;
10511072
TransferredNonTransferrableInfo info;
10521073
std::optional<UseDiagnosticInfo> diagnosticInfo;
10531074
SourceLoc loc;
10541075

10551076
public:
10561077
TransferNonTransferrableDiagnosticInferrer(
1057-
TransferredNonTransferrableInfo info)
1058-
: info(info),
1078+
RegionAnalysisValueMap &valueMap, TransferredNonTransferrableInfo info)
1079+
: valueMap(valueMap), info(info),
10591080
loc(info.transferredOperand->getUser()->getLoc().getSourceLoc()) {}
10601081

10611082
/// Gathers diagnostics. Returns false if we emitted a "I don't understand
@@ -1099,6 +1120,28 @@ bool TransferNonTransferrableDiagnosticInferrer::run() {
10991120
auto *op = info.transferredOperand;
11001121
auto loc = info.transferredOperand->getUser()->getLoc();
11011122

1123+
// Before we do anything, see if the transfer instruction was into a
1124+
// transferring parameter alloc_stack. In such a case, we emit a special
1125+
// message.
1126+
if (auto destValue = getDestOfStoreOrCopyAddr(op)) {
1127+
auto trackedValue = valueMap.getTrackableValue(destValue);
1128+
if (trackedValue.isTransferringParameter()) {
1129+
auto valueName = inferNameFromValue(op->get());
1130+
auto paramName =
1131+
inferNameFromValue(trackedValue.getRepresentative().getValue());
1132+
if (!valueName || !paramName) {
1133+
diagnoseError(op->getUser(),
1134+
diag::regionbasedisolation_unknown_pattern);
1135+
return false;
1136+
}
1137+
1138+
diagnosticInfo = UseDiagnosticInfo::
1139+
forFunctionArgumentAssignedToStronglyTransferredParam(*valueName,
1140+
*paramName);
1141+
return true;
1142+
}
1143+
}
1144+
11021145
if (auto *sourceApply = loc.getAsASTNode<ApplyExpr>()) {
11031146
std::optional<ApplyIsolationCrossing> isolation = {};
11041147

@@ -1182,7 +1225,8 @@ void TransferNonSendableImpl::emitTransferredNonTransferrableDiagnostics() {
11821225
auto &astContext = regionInfo->getFunction()->getASTContext();
11831226
for (auto info : transferredNonTransferrable) {
11841227
auto *op = info.transferredOperand;
1185-
TransferNonTransferrableDiagnosticInferrer diagnosticInferrer(info);
1228+
TransferNonTransferrableDiagnosticInferrer diagnosticInferrer(
1229+
regionInfo->getValueMap(), info);
11861230
if (!diagnosticInferrer.run())
11871231
continue;
11881232

@@ -1254,6 +1298,20 @@ void TransferNonSendableImpl::emitTransferredNonTransferrableDiagnostics() {
12541298
diagnosticInfo.getIsolationCrossing().getCallerIsolation(),
12551299
diagnosticInfo.getIsolationCrossing().getCalleeIsolation());
12561300
break;
1301+
case UseDiagnosticInfoKind::
1302+
FunctionArgumentAssignedToStronglyTransferredParam:
1303+
diagnoseError(
1304+
astContext, loc,
1305+
diag::
1306+
regionbasedisolation_stronglytransfer_assignment_yields_race_name,
1307+
diagnosticInfo.getName(),
1308+
diagnosticInfo.getTransferredParamIdentifier());
1309+
diagnoseNote(
1310+
astContext, loc,
1311+
diag::regionbasedisolation_stronglytransfer_taskisolated_assign_note,
1312+
diagnosticInfo.getName(),
1313+
diagnosticInfo.getTransferredParamIdentifier());
1314+
break;
12571315
}
12581316
}
12591317
}

test/Concurrency/transfernonsendable_strong_transferring_params.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,26 @@ struct NonSendableStruct {
1313
var second = Klass()
1414
}
1515

16+
class KlassWithNonSendableStructPair {
17+
var ns1: NonSendableStruct
18+
var ns2: (NonSendableStruct, NonSendableStruct)
19+
20+
init() {
21+
ns1 = NonSendableStruct()
22+
ns2 = (ns1, ns1)
23+
}
24+
}
25+
26+
final class FinalKlassWithNonSendableStructPair {
27+
var ns1: NonSendableStruct
28+
var ns2: (NonSendableStruct, NonSendableStruct)
29+
30+
init() {
31+
ns1 = NonSendableStruct()
32+
ns2 = (ns1, ns1)
33+
}
34+
}
35+
1636
func useValue<T>(_ t: T) {}
1737
func getAny() -> Any { fatalError() }
1838

@@ -299,3 +319,30 @@ func testTransferSrc(_ x: transferring Klass) async {
299319
await transferToMain(y) // expected-warning {{transferring value of non-Sendable type 'Klass' from nonisolated context to main actor-isolated context}}
300320
x = y // expected-note {{access here could race}}
301321
}
322+
323+
func testTransferOtherParam(_ x: transferring Klass, y: Klass) async {
324+
x = y // expected-warning {{assigning 'y' to transferring parameter 'x' may cause a race}}
325+
// expected-note @-1 {{'y' is a task isolated value that is assigned into transferring parameter 'x'. Transferred uses of 'x' may race with caller uses of 'y'}}
326+
}
327+
328+
func testTransferOtherParamTuple(_ x: transferring Klass, y: (Klass, Klass)) async {
329+
x = y.0 // expected-warning {{assigning 'y.0' to transferring parameter 'x' may cause a race}}
330+
// expected-note @-1 {{'y.0' is a task isolated value that is assigned into transferring parameter 'x'. Transferred uses of 'x' may race with caller uses of 'y.0'}}
331+
}
332+
333+
func testTransferOtherParamStruct(_ x: transferring Klass, y: NonSendableStruct) async {
334+
x = y.first // expected-warning {{assigning 'y.first' to transferring parameter 'x' may cause a race}}
335+
// expected-note @-1 {{'y.first' is a task isolated value that is assigned into transferring parameter 'x'. Transferred uses of 'x' may race with caller uses of 'y.first'}}
336+
}
337+
338+
func testTransferOtherParamTupleStruct(_ x: transferring Klass, y: (NonSendableStruct, NonSendableStruct)) async {
339+
x = y.1.second // expected-warning {{assigning 'y.1.second' to transferring parameter 'x' may cause a race}}
340+
// expected-note @-1 {{'y.1.second' is a task isolated value that is assigned into transferring parameter 'x'. Transferred uses of 'x' may race with caller uses of 'y.1.second'}}
341+
}
342+
343+
func testTransferOtherParamClassStructTuple(_ x: transferring Klass, y: KlassWithNonSendableStructPair) async {
344+
x = y.ns1.second // expected-warning {{assigning 'y.ns1.second' to transferring parameter 'x' may cause a race}}
345+
// expected-note @-1 {{'y.ns1.second' is a task isolated value that is assigned into transferring parameter 'x'}}
346+
x = y.ns2.1.second // expected-warning {{assigning 'y.ns2.1.second' to transferring parameter 'x' may cause a race}}
347+
// expected-note @-1 {{'y.ns2.1.second' is a task isolated value that is assigned into transferring parameter 'x'. Transferred uses of 'x' may race with caller uses of 'y.ns2.1.second'}}
348+
}

test/Concurrency/transfernonsendable_warning_until_swift6.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ func testAssignmentIntoTransferringParameter(_ x: transferring NonSendableType)
3939
}
4040

4141
func testAssigningParameterIntoTransferringParameter(_ x: transferring NonSendableType, _ y: NonSendableType) async {
42-
x = y // expected-error {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
42+
x = y // expected-error {{assigning 'y' to transferring parameter 'x' may cause a race}}
43+
// expected-note @-1 {{'y' is a task isolated value that is assigned into transferring parameter 'x'. Transferred uses of 'x' may race with caller uses of 'y'}}
4344
}
4445

4546
func testIsolationCrossingDueToCapture() async {

0 commit comments

Comments
 (0)