Skip to content

Commit 1c193ca

Browse files
committed
[region-isolation] Eliminate more "call site passes self" warnings
I just did a full pass through. There were some cases around nonisolated closures defined in methods and global actor isolated things where we are now emitting the wrong message. I am going to fix that in subsequent commits.
1 parent e1ecd8b commit 1c193ca

9 files changed

+276
-150
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,9 @@ ERROR(regionbasedisolation_named_transfer_yields_race, none,
976976
NOTE(regionbasedisolation_named_info_transfer_yields_race, none,
977977
"%0 is transferred from %1 caller to %2 callee. Later uses in caller could race with potential uses in callee",
978978
(Identifier, ActorIsolation, ActorIsolation))
979+
NOTE(regionbasedisolation_transfer_non_transferrable_named_note, none,
980+
"transferring %1 %0 to %2 callee could cause races between %2 and %1 uses",
981+
(Identifier, ActorIsolation, ActorIsolation))
979982
NOTE(regionbasedisolation_named_info_isolated_capture, none,
980983
"%1 value %0 is captured by %2 closure. Later local uses could race",
981984
(Identifier, ActorIsolation, ActorIsolation))

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 118 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,43 @@ using Region = PartitionPrimitives::Region;
5151

5252
} // namespace
5353

54+
//===----------------------------------------------------------------------===//
55+
// MARK: Utilities
56+
//===----------------------------------------------------------------------===//
57+
58+
static Expr *inferArgumentExprFromApplyExpr(ApplyExpr *sourceApply,
59+
FullApplySite fai,
60+
const Operand *op) {
61+
62+
Expr *foundExpr = nullptr;
63+
64+
// If we have self, then infer it.
65+
if (fai.hasSelfArgument() && op == &fai.getSelfArgumentOperand()) {
66+
if (auto callExpr = dyn_cast<CallExpr>(sourceApply))
67+
if (auto calledExpr =
68+
dyn_cast<DotSyntaxCallExpr>(callExpr->getDirectCallee()))
69+
foundExpr = calledExpr->getBase();
70+
} else {
71+
// Otherwise, try to infer using the operand of the ApplyExpr.
72+
unsigned argNum = [&]() -> unsigned {
73+
if (fai.isCalleeOperand(*op))
74+
return op->getOperandNumber();
75+
return fai.getAppliedArgIndex(*op);
76+
}();
77+
assert(argNum < sourceApply->getArgs()->size());
78+
79+
foundExpr = sourceApply->getArgs()->getExpr(argNum);
80+
81+
// If we have an erasure expression, lets use the original type. We do
82+
// this since we are not saying the specific parameter that is the
83+
// issue and we are using the type to explain it to the user.
84+
if (auto *erasureExpr = dyn_cast<ErasureExpr>(foundExpr))
85+
foundExpr = erasureExpr->getSubExpr();
86+
}
87+
88+
return foundExpr;
89+
}
90+
5491
//===----------------------------------------------------------------------===//
5592
// MARK: Diagnostics
5693
//===----------------------------------------------------------------------===//
@@ -578,22 +615,7 @@ void UseAfterTransferDiagnosticInferrer::initForApply(const Operand *op,
578615

579616
assert(!fai.getArgumentConvention(*op).isIndirectOutParameter() &&
580617
"An indirect out parameter is never transferred");
581-
582-
Expr *foundExpr = nullptr;
583-
584-
// If we have self, then infer it.
585-
if (fai.hasSelfArgument() && op == &fai.getSelfArgumentOperand()) {
586-
foundExpr = getFoundExprForSelf(sourceApply);
587-
} else {
588-
// Otherwise, try to infer using the operand of the ApplyExpr.
589-
unsigned argNum = [&]() -> unsigned {
590-
if (fai.isCalleeOperand(*op))
591-
return op->getOperandNumber();
592-
return fai.getAppliedArgIndex(*op);
593-
}();
594-
assert(argNum < sourceApply->getArgs()->size());
595-
foundExpr = getFoundExprForParam(sourceApply, argNum);
596-
}
618+
auto *foundExpr = inferArgumentExprFromApplyExpr(sourceApply, fai, op);
597619

598620
auto inferredArgType =
599621
foundExpr ? foundExpr->findOriginalType() : baseInferredType;
@@ -727,7 +749,9 @@ void UseAfterTransferDiagnosticInferrer::init(const Operand *op) {
727749
// Before we do anything further, see if we can find a name and emit a name
728750
// error.
729751
SmallString<64> resultingName;
730-
VariableNameInferrer inferrer(op->getFunction(), resultingName);
752+
VariableNameInferrer::Options options;
753+
options |= VariableNameInferrer::Flag::InferSelfThroughAllAccessors;
754+
VariableNameInferrer inferrer(op->getFunction(), options, resultingName);
731755
auto &astContext = op->getFunction()->getASTContext();
732756
if (auto rootValue =
733757
inferrer.inferByWalkingUsesToDefsReturningRoot(op->get())) {
@@ -946,19 +970,41 @@ class TransferNonTransferrableDiagnosticInferrer {
946970
/// Used if we have a function argument passed as an explicitly strongly
947971
/// transferring argument to a function.
948972
FunctionArgumentApplyStronglyTransferred = 4,
973+
974+
/// Used if we have a named variable.
975+
NamedIsolation = 5,
949976
};
950977

951-
struct UseDiagnosticInfo {
978+
class UseDiagnosticInfo {
952979
UseDiagnosticInfoKind kind;
953980
std::optional<ApplyIsolationCrossing> transferredIsolationCrossing = {};
981+
llvm::PointerUnion<Type, Identifier> inferredTypeOrIdentifier = {};
982+
983+
public:
984+
UseDiagnosticInfoKind getKind() const { return kind; }
985+
ApplyIsolationCrossing getIsolationCrossing() const {
986+
return transferredIsolationCrossing.value();
987+
}
988+
Identifier getName() const {
989+
return inferredTypeOrIdentifier.get<Identifier>();
990+
}
991+
Type getType() const { return inferredTypeOrIdentifier.get<Type>(); }
954992

955993
static UseDiagnosticInfo forMiscUse() {
956994
return {UseDiagnosticInfoKind::MiscUse};
957995
}
958996

959997
static UseDiagnosticInfo
960-
forFunctionArgumentApply(ApplyIsolationCrossing isolation) {
961-
return {UseDiagnosticInfoKind::FunctionArgumentApply, isolation};
998+
forNamed(Identifier valueName, ApplyIsolationCrossing isolationCrossing) {
999+
return {UseDiagnosticInfoKind::NamedIsolation, isolationCrossing,
1000+
valueName};
1001+
}
1002+
1003+
static UseDiagnosticInfo
1004+
forFunctionArgumentApply(ApplyIsolationCrossing isolation,
1005+
Type inferredType) {
1006+
return {UseDiagnosticInfoKind::FunctionArgumentApply, isolation,
1007+
inferredType};
9621008
}
9631009

9641010
static UseDiagnosticInfo
@@ -971,9 +1017,12 @@ class TransferNonTransferrableDiagnosticInferrer {
9711017
}
9721018

9731019
private:
974-
UseDiagnosticInfo(UseDiagnosticInfoKind kind,
975-
std::optional<ApplyIsolationCrossing> isolation = {})
976-
: kind(kind), transferredIsolationCrossing(isolation) {}
1020+
UseDiagnosticInfo(
1021+
UseDiagnosticInfoKind kind,
1022+
std::optional<ApplyIsolationCrossing> isolation = {},
1023+
llvm::PointerUnion<Type, Identifier> inferredTypeOrIdentifier = {})
1024+
: kind(kind), transferredIsolationCrossing(isolation),
1025+
inferredTypeOrIdentifier(inferredTypeOrIdentifier) {}
9771026
};
9781027

9791028
private:
@@ -1024,15 +1073,12 @@ bool TransferNonTransferrableDiagnosticInferrer::initForIsolatedPartialApply(
10241073
}
10251074

10261075
bool TransferNonTransferrableDiagnosticInferrer::run() {
1027-
if (!isa<SILFunctionArgument>(info.nonTransferrableValue)) {
1028-
diagnosticInfo = UseDiagnosticInfo::forMiscUse();
1029-
return true;
1030-
}
1031-
10321076
// We need to find the isolation info.
10331077
auto loc = info.transferredOperand->getUser()->getLoc();
10341078

10351079
if (auto *sourceApply = loc.getAsASTNode<ApplyExpr>()) {
1080+
auto *op = info.transferredOperand;
1081+
10361082
std::optional<ApplyIsolationCrossing> isolation = {};
10371083

10381084
// First try to get the apply from the isolation crossing.
@@ -1042,9 +1088,9 @@ bool TransferNonTransferrableDiagnosticInferrer::run() {
10421088
// If we could not infer an isolation...
10431089
if (!isolation) {
10441090
// First see if we have a transferring argument.
1045-
if (auto fas = FullApplySite::isa(info.transferredOperand->getUser())) {
1046-
if (fas.getArgumentParameterInfo(*info.transferredOperand)
1047-
.hasOption(SILParameterInfo::Transferring)) {
1091+
if (auto fas = FullApplySite::isa(op->getUser())) {
1092+
if (fas.getArgumentParameterInfo(*op).hasOption(
1093+
SILParameterInfo::Transferring)) {
10481094
diagnosticInfo =
10491095
UseDiagnosticInfo::forFunctionArgumentApplyStronglyTransferred();
10501096
return true;
@@ -1053,12 +1099,35 @@ bool TransferNonTransferrableDiagnosticInferrer::run() {
10531099

10541100
// Otherwise, emit a "we don't know error" that tells the user to file a
10551101
// bug.
1056-
diagnoseError(info.transferredOperand->getUser(),
1057-
diag::regionbasedisolation_unknown_pattern);
1102+
diagnoseError(op->getUser(), diag::regionbasedisolation_unknown_pattern);
10581103
return false;
10591104
}
1105+
assert(isolation && "Expected non-null");
1106+
1107+
// See if we can infer a name from the value.
1108+
SmallString<64> resultingName;
1109+
VariableNameInferrer::Options options;
1110+
options |= VariableNameInferrer::Flag::InferSelfThroughAllAccessors;
1111+
VariableNameInferrer inferrer(op->getFunction(), options, resultingName);
1112+
if (inferrer.inferByWalkingUsesToDefsReturningRoot(op->get())) {
1113+
auto &astContext = op->getFunction()->getASTContext();
1114+
diagnosticInfo = UseDiagnosticInfo::forNamed(
1115+
astContext.getIdentifier(inferrer.getName()), *isolation);
1116+
return true;
1117+
}
10601118

1061-
diagnosticInfo = UseDiagnosticInfo::forFunctionArgumentApply(*isolation);
1119+
// Attempt to find the specific sugared ASTType if we can to emit a better
1120+
// diagnostic.
1121+
Type type = op->get()->getType().getASTType();
1122+
if (auto fas = FullApplySite::isa(info.transferredOperand->getUser())) {
1123+
if (auto *inferredArgExpr =
1124+
inferArgumentExprFromApplyExpr(sourceApply, fas, op)) {
1125+
type = inferredArgExpr->findOriginalType();
1126+
}
1127+
}
1128+
1129+
diagnosticInfo =
1130+
UseDiagnosticInfo::forFunctionArgumentApply(*isolation, type);
10621131
return true;
10631132
}
10641133

@@ -1094,7 +1163,7 @@ void TransferNonSendableImpl::emitTransferredNonTransferrableDiagnostics() {
10941163

10951164
auto diagnosticInfo = diagnosticInferrer.getDiagnostic();
10961165
auto loc = diagnosticInferrer.getLoc();
1097-
switch (diagnosticInfo.kind) {
1166+
switch (diagnosticInfo.getKind()) {
10981167
case UseDiagnosticInfoKind::Invalid:
10991168
llvm_unreachable("Should never see this");
11001169
case UseDiagnosticInfoKind::MiscUse:
@@ -1103,9 +1172,8 @@ void TransferNonSendableImpl::emitTransferredNonTransferrableDiagnostics() {
11031172
break;
11041173
case UseDiagnosticInfoKind::FunctionArgumentApply: {
11051174
diagnoseError(astContext, loc, diag::regionbasedisolation_arg_transferred,
1106-
op->get()->getType().getASTType(),
1107-
diagnosticInfo.transferredIsolationCrossing.value()
1108-
.getCalleeIsolation())
1175+
diagnosticInfo.getType(),
1176+
diagnosticInfo.getIsolationCrossing().getCalleeIsolation())
11091177
.highlight(op->getUser()->getLoc().getSourceRange());
11101178
// Only emit the note if our value is different from the function
11111179
// argument.
@@ -1124,8 +1192,7 @@ void TransferNonSendableImpl::emitTransferredNonTransferrableDiagnostics() {
11241192
case UseDiagnosticInfoKind::FunctionArgumentClosure: {
11251193
diagnoseError(astContext, loc, diag::regionbasedisolation_arg_transferred,
11261194
op->get()->getType().getASTType(),
1127-
diagnosticInfo.transferredIsolationCrossing.value()
1128-
.getCalleeIsolation())
1195+
diagnosticInfo.getIsolationCrossing().getCalleeIsolation())
11291196
.highlight(op->getUser()->getLoc().getSourceRange());
11301197
// Only emit the note if our value is different from the function
11311198
// argument.
@@ -1149,6 +1216,17 @@ void TransferNonSendableImpl::emitTransferredNonTransferrableDiagnostics() {
11491216
.highlight(op->getUser()->getLoc().getSourceRange());
11501217
break;
11511218
}
1219+
case UseDiagnosticInfoKind::NamedIsolation:
1220+
diagnoseError(astContext, loc,
1221+
diag::regionbasedisolation_named_transfer_yields_race,
1222+
diagnosticInfo.getName());
1223+
diagnoseNote(
1224+
astContext, loc,
1225+
diag::regionbasedisolation_transfer_non_transferrable_named_note,
1226+
diagnosticInfo.getName(),
1227+
diagnosticInfo.getIsolationCrossing().getCallerIsolation(),
1228+
diagnosticInfo.getIsolationCrossing().getCalleeIsolation());
1229+
break;
11521230
}
11531231
}
11541232
}

test/Concurrency/isolated_captures.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class NotSendable {
3939
let ns = NotSendable(x: 0)
4040
MyActor.ns = ns
4141

42-
// expected-region-isolation-warning@+1 {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller; this is an error in Swift 6}}
42+
// expected-region-isolation-warning @+2 {{transferring 'ns' could cause a race}}
43+
// expected-region-isolation-note @+1 {{transferring global actor 'MyActor'-isolated 'ns' to global actor 'YourActor'-isolated callee could cause races between global actor 'YourActor'-isolated and global actor 'MyActor'-isolated uses}}
4344
await { @YourActor in
4445
// expected-complete-warning@+1 {{capture of 'ns' with non-sendable type 'NotSendable' in an isolated closure; this is an error in Swift 6}}
4546
YourActor.ns = ns

test/Concurrency/sendable_checking.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,13 @@ final class NonSendable {
251251
func call() async {
252252
await update()
253253
// expected-targeted-and-complete-warning @-1 {{passing argument of non-sendable type 'NonSendable' into main actor-isolated context may introduce data races}}
254-
// expected-tns-warning @-2 {{task isolated value of type 'NonSendable' transferred to main actor-isolated context; later accesses to value could race}}
254+
// expected-tns-warning @-2 {{transferring 'self' could cause a race}}
255+
// expected-tns-note @-3 {{transferring nonisolated 'self' to main actor-isolated callee could cause races between main actor-isolated and nonisolated uses}}
255256

256257
await self.update()
257258
// expected-targeted-and-complete-warning @-1 {{passing argument of non-sendable type 'NonSendable' into main actor-isolated context may introduce data races}}
258-
// expected-tns-warning @-2 {{task isolated value of type 'NonSendable' transferred to main actor-isolated context; later accesses to value could race}}
259+
// expected-tns-warning @-2 {{transferring 'self' could cause a race}}
260+
// expected-tns-note @-3 {{transferring nonisolated 'self' to main actor-isolated callee could cause races between main actor-isolated and nonisolated uses}}
259261

260262
_ = await x
261263
// expected-warning@-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to main actor-isolated property 'x' cannot cross actor boundary}}
@@ -293,13 +295,14 @@ func callNonisolatedAsyncClosure(
293295
) async {
294296
await g(ns)
295297
// expected-targeted-and-complete-warning @-1 {{passing argument of non-sendable type 'NonSendable' outside of main actor-isolated context may introduce data races}}
296-
// expected-tns-warning @-2 {{task isolated value of type 'NonSendable' transferred to nonisolated context; later accesses to value could race}}
298+
// expected-tns-warning @-2 {{transferring 'ns' could cause a race}}
299+
// expected-tns-note @-3 {{transferring main actor-isolated 'ns' to nonisolated callee could cause races between nonisolated and main actor-isolated uses}}
297300

298301
let f: (NonSendable) async -> () = globalSendable // okay
299302
await f(ns)
300303
// expected-targeted-and-complete-warning@-1 {{passing argument of non-sendable type 'NonSendable' outside of main actor-isolated context may introduce data races}}
301-
// expected-tns-warning @-2 {{task isolated value of type 'NonSendable' transferred to nonisolated context; later accesses to value could race}}
302-
304+
// expected-tns-warning @-2 {{transferring 'ns' could cause a race}}
305+
// expected-tns-note @-3 {{transferring main actor-isolated 'ns' to nonisolated callee could cause races between nonisolated and main actor-isolated uses}}
303306
}
304307

305308
@available(SwiftStdlib 5.1, *)

0 commit comments

Comments
 (0)