Skip to content

Commit 49eee05

Browse files
committed
[region-isolation] Treat as Sendable values that are meant to be ignored since they are marked with preconcurrency
rdar://133531625
1 parent bbc4816 commit 49eee05

File tree

8 files changed

+249
-111
lines changed

8 files changed

+249
-111
lines changed

include/swift/SIL/SILType.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,11 @@ class SILType {
964964
bool isAnyActor() const { return getASTType()->isAnyActorType(); }
965965

966966
/// Returns true if this function conforms to the Sendable protocol.
967+
///
968+
/// NOTE: For diagnostics this is not always the correct thing to check since
969+
/// non-Sendable types afflicted with preconcurrency can have different
970+
/// semantic requirements around diagnostics. \see
971+
/// getConcurrencyDiagnosticBehavior.
967972
bool isSendable(SILFunction *fn) const;
968973

969974
/// False if SILValues of this type cannot be used outside the scope of their
@@ -978,6 +983,16 @@ class SILType {
978983
return !isNoEscapeFunction() && isEscapable(function);
979984
}
980985

986+
/// Return the expected concurrency diagnostic behavior for this SILType.
987+
///
988+
/// This allows one to know if the type is marked with preconcurrency and thus
989+
/// should have diagnostics ignored or converted to warnings instead of
990+
/// errors.
991+
///
992+
/// \returns nil if we were unable to find such information for this type.
993+
std::optional<DiagnosticBehavior>
994+
getConcurrencyDiagnosticBehavior(SILFunction *fn) const;
995+
981996
//
982997
// Accessors for types used in SIL instructions:
983998
//

lib/AST/Type.cpp

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,37 @@
1616

1717
#define DEBUG_TYPE "ast-types"
1818

19-
#include "swift/AST/Types.h"
19+
#include "clang/AST/Type.h"
2020
#include "ForeignRepresentationInfo.h"
2121
#include "swift/AST/ASTContext.h"
2222
#include "swift/AST/ClangModuleLoader.h"
23+
#include "swift/AST/Concurrency.h"
2324
#include "swift/AST/ConformanceLookup.h"
24-
#include "swift/AST/ExistentialLayout.h"
25-
#include "swift/AST/ReferenceCounting.h"
26-
#include "swift/AST/TypeCheckRequests.h"
27-
#include "swift/AST/TypeVisitor.h"
28-
#include "swift/AST/TypeWalker.h"
2925
#include "swift/AST/Decl.h"
26+
#include "swift/AST/ExistentialLayout.h"
3027
#include "swift/AST/GenericEnvironment.h"
3128
#include "swift/AST/LazyResolver.h"
3229
#include "swift/AST/Module.h"
3330
#include "swift/AST/PackConformance.h"
3431
#include "swift/AST/ParameterList.h"
3532
#include "swift/AST/PrettyStackTrace.h"
3633
#include "swift/AST/ProtocolConformance.h"
34+
#include "swift/AST/ReferenceCounting.h"
3735
#include "swift/AST/SILLayout.h"
3836
#include "swift/AST/SubstitutionMap.h"
37+
#include "swift/AST/TypeCheckRequests.h"
3938
#include "swift/AST/TypeLoc.h"
4039
#include "swift/AST/TypeRepr.h"
4140
#include "swift/AST/TypeTransform.h"
41+
#include "swift/AST/TypeVisitor.h"
42+
#include "swift/AST/TypeWalker.h"
43+
#include "swift/AST/Types.h"
4244
#include "swift/Basic/Assertions.h"
4345
#include "swift/Basic/Compiler.h"
44-
#include "clang/AST/Type.h"
4546
#include "llvm/ADT/APFloat.h"
47+
#include "llvm/ADT/STLExtras.h"
4648
#include "llvm/ADT/SmallPtrSet.h"
4749
#include "llvm/ADT/SmallString.h"
48-
#include "llvm/ADT/STLExtras.h"
4950
#include "llvm/Support/Compiler.h"
5051
#include "llvm/Support/Debug.h"
5152
#include "llvm/Support/raw_ostream.h"
@@ -4883,3 +4884,50 @@ StringRef swift::getNameForParamSpecifier(ParamSpecifier specifier) {
48834884
return "implicitly_copyable_consuming";
48844885
}
48854886
}
4887+
4888+
std::optional<DiagnosticBehavior>
4889+
TypeBase::getConcurrencyDiagnosticBehaviorLimit(DeclContext *declCtx) const {
4890+
auto *self = const_cast<TypeBase *>(this);
4891+
4892+
if (auto *nomDecl = self->getNominalOrBoundGenericNominal()) {
4893+
// First try to just grab the exact concurrency diagnostic behavior.
4894+
if (auto result =
4895+
swift::getConcurrencyDiagnosticBehaviorLimit(nomDecl, declCtx)) {
4896+
return result;
4897+
}
4898+
4899+
// But if we get nothing, see if we can come up with diagnostic behavior by
4900+
// merging our fields if we have a struct.
4901+
if (auto *structDecl = dyn_cast<StructDecl>(nomDecl)) {
4902+
std::optional<DiagnosticBehavior> diagnosticBehavior;
4903+
auto substMap = self->getContextSubstitutionMap();
4904+
for (auto storedProperty : structDecl->getStoredProperties()) {
4905+
auto lhs = diagnosticBehavior.value_or(DiagnosticBehavior::Unspecified);
4906+
auto astType = storedProperty->getInterfaceType().subst(substMap);
4907+
auto rhs = astType->getConcurrencyDiagnosticBehaviorLimit(declCtx);
4908+
auto result = lhs.merge(rhs.value_or(DiagnosticBehavior::Unspecified));
4909+
if (result != DiagnosticBehavior::Unspecified)
4910+
diagnosticBehavior = result;
4911+
}
4912+
return diagnosticBehavior;
4913+
}
4914+
}
4915+
4916+
// When attempting to determine the diagnostic behavior limit of a tuple, just
4917+
// merge for each of the elements.
4918+
if (auto *tupleType = self->getAs<TupleType>()) {
4919+
std::optional<DiagnosticBehavior> diagnosticBehavior;
4920+
for (auto tupleType : tupleType->getElements()) {
4921+
auto lhs = diagnosticBehavior.value_or(DiagnosticBehavior::Unspecified);
4922+
4923+
auto type = tupleType.getType()->getCanonicalType();
4924+
auto rhs = type->getConcurrencyDiagnosticBehaviorLimit(declCtx);
4925+
auto result = lhs.merge(rhs.value_or(DiagnosticBehavior::Unspecified));
4926+
if (result != DiagnosticBehavior::Unspecified)
4927+
diagnosticBehavior = result;
4928+
}
4929+
return diagnosticBehavior;
4930+
}
4931+
4932+
return {};
4933+
}

lib/SIL/IR/SILType.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "swift/SIL/SILModule.h"
2626
#include "swift/SIL/Test.h"
2727
#include "swift/SIL/TypeLowering.h"
28+
#include "swift/Sema/Concurrency.h"
2829
#include <tuple>
2930

3031
using namespace swift;
@@ -1255,6 +1256,15 @@ bool SILType::isSendable(SILFunction *fn) const {
12551256
return getASTType()->isSendableType();
12561257
}
12571258

1259+
std::optional<DiagnosticBehavior>
1260+
SILType::getConcurrencyDiagnosticBehavior(SILFunction *fn) const {
1261+
auto declRef = fn->getDeclRef();
1262+
if (!declRef)
1263+
return {};
1264+
auto *fromDC = declRef.getInnermostDeclContext();
1265+
return getASTType()->getConcurrencyDiagnosticBehaviorLimit(fromDC);
1266+
}
1267+
12581268
namespace swift::test {
12591269
// Arguments:
12601270
// - SILValue: value

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -94,39 +94,23 @@ static SILValue stripFunctionConversions(SILValue val) {
9494
}
9595

9696
static std::optional<DiagnosticBehavior>
97-
getDiagnosticBehaviorLimitForValue(SILValue value) {
98-
auto *nom = value->getType().getNominalOrBoundGenericNominal();
99-
if (!nom)
100-
return {};
101-
102-
auto declRef = value->getFunction()->getDeclRef();
103-
if (!declRef)
104-
return {};
105-
106-
auto *fromDC = declRef.getInnermostDeclContext();
107-
return getConcurrencyDiagnosticBehaviorLimit(nom, fromDC);
108-
}
109-
110-
static std::optional<DiagnosticBehavior>
111-
getDiagnosticBehaviorLimitForCapturedValue(CapturedValue value) {
97+
getDiagnosticBehaviorLimitForCapturedValue(SILFunction *fn,
98+
CapturedValue value) {
11299
ValueDecl *decl = value.getDecl();
113-
auto *nom = decl->getInterfaceType()->getNominalOrBoundGenericNominal();
114-
if (!nom)
115-
return {};
116-
117-
auto *fromDC = decl->getInnermostDeclContext();
118-
return getConcurrencyDiagnosticBehaviorLimit(nom, fromDC);
100+
auto *ctx = decl->getInnermostDeclContext();
101+
auto type = fn->mapTypeIntoContext(decl->getInterfaceType());
102+
return type->getConcurrencyDiagnosticBehaviorLimit(ctx);
119103
}
120104

121105
/// Find the most conservative diagnostic behavior by taking the max over all
122106
/// DiagnosticBehavior for the captured values.
123107
static std::optional<DiagnosticBehavior>
124108
getDiagnosticBehaviorLimitForCapturedValues(
125-
ArrayRef<CapturedValue> capturedValues) {
109+
SILFunction *fn, ArrayRef<CapturedValue> capturedValues) {
126110
std::optional<DiagnosticBehavior> diagnosticBehavior;
127111
for (auto value : capturedValues) {
128112
auto lhs = diagnosticBehavior.value_or(DiagnosticBehavior::Unspecified);
129-
auto rhs = getDiagnosticBehaviorLimitForCapturedValue(value).value_or(
113+
auto rhs = getDiagnosticBehaviorLimitForCapturedValue(fn, value).value_or(
130114
DiagnosticBehavior::Unspecified);
131115
auto result = lhs.merge(rhs);
132116
if (result != DiagnosticBehavior::Unspecified)
@@ -668,8 +652,11 @@ class UseAfterTransferDiagnosticEmitter {
668652
emitUnknownPatternError();
669653
}
670654

655+
SILFunction *getFunction() const { return transferOp->getFunction(); }
656+
671657
std::optional<DiagnosticBehavior> getBehaviorLimit() const {
672-
return getDiagnosticBehaviorLimitForValue(transferOp->get());
658+
return transferOp->get()->getType().getConcurrencyDiagnosticBehavior(
659+
getFunction());
673660
}
674661

675662
/// If we can find a callee decl name, return that. None otherwise.
@@ -1329,6 +1316,8 @@ class TransferNonTransferrableDiagnosticEmitter {
13291316

13301317
Operand *getOperand() const { return info.transferredOperand; }
13311318

1319+
SILFunction *getFunction() const { return getOperand()->getFunction(); }
1320+
13321321
SILValue getNonTransferrableValue() const {
13331322
return info.nonTransferrable.dyn_cast<SILValue>();
13341323
}
@@ -1338,7 +1327,9 @@ class TransferNonTransferrableDiagnosticEmitter {
13381327
}
13391328

13401329
std::optional<DiagnosticBehavior> getBehaviorLimit() const {
1341-
return getDiagnosticBehaviorLimitForValue(info.transferredOperand->get());
1330+
return info.transferredOperand->get()
1331+
->getType()
1332+
.getConcurrencyDiagnosticBehavior(getOperand()->getFunction());
13421333
}
13431334

13441335
/// If we can find a callee decl name, return that. None otherwise.
@@ -1460,8 +1451,8 @@ class TransferNonTransferrableDiagnosticEmitter {
14601451
diag::regionbasedisolation_typed_tns_passed_sending_closure,
14611452
descriptiveKindStr)
14621453
.highlight(loc.getSourceRange())
1463-
.limitBehaviorIf(
1464-
getDiagnosticBehaviorLimitForCapturedValue(capturedValue));
1454+
.limitBehaviorIf(getDiagnosticBehaviorLimitForCapturedValue(
1455+
getFunction(), capturedValue));
14651456

14661457
auto capturedLoc = RegularLocation(capturedValue.getLoc());
14671458
if (getIsolationRegionInfo().getIsolationInfo().isTaskIsolated()) {
@@ -1496,8 +1487,8 @@ class TransferNonTransferrableDiagnosticEmitter {
14961487
}
14971488
}
14981489

1499-
auto behaviorLimit =
1500-
getDiagnosticBehaviorLimitForCapturedValues(capturedValues);
1490+
auto behaviorLimit = getDiagnosticBehaviorLimitForCapturedValues(
1491+
getFunction(), capturedValues);
15011492
diagnoseError(loc,
15021493
diag::regionbasedisolation_typed_tns_passed_sending_closure,
15031494
descriptiveKindStr)
@@ -2059,8 +2050,13 @@ class InOutSendingNotDisconnectedDiagnosticEmitter {
20592050
emitUnknownPatternError();
20602051
}
20612052

2053+
SILFunction *getFunction() const {
2054+
return info.inoutSendingParam->getFunction();
2055+
}
2056+
20622057
std::optional<DiagnosticBehavior> getBehaviorLimit() const {
2063-
return getDiagnosticBehaviorLimitForValue(info.inoutSendingParam);
2058+
return info.inoutSendingParam->getType().getConcurrencyDiagnosticBehavior(
2059+
getFunction());
20642060
}
20652061

20662062
void emitUnknownPatternError() {
@@ -2178,8 +2174,11 @@ class AssignIsolatedIntoSendingResultDiagnosticEmitter {
21782174
emitUnknownPatternError();
21792175
}
21802176

2181-
std::optional<DiagnosticBehavior> getBehaviorLimit() const {
2182-
return getDiagnosticBehaviorLimitForValue(info.outSendingResult);
2177+
SILFunction *getFunction() const { return info.srcOperand->getFunction(); }
2178+
2179+
std::optional<DiagnosticBehavior> getConcurrencyDiagnosticBehavior() const {
2180+
return info.outSendingResult->getType().getConcurrencyDiagnosticBehavior(
2181+
getFunction());
21832182
}
21842183

21852184
void emitUnknownPatternError() {
@@ -2190,7 +2189,7 @@ class AssignIsolatedIntoSendingResultDiagnosticEmitter {
21902189

21912190
diagnoseError(info.srcOperand->getUser(),
21922191
diag::regionbasedisolation_unknown_pattern)
2193-
.limitBehaviorIf(getBehaviorLimit());
2192+
.limitBehaviorIf(getConcurrencyDiagnosticBehavior());
21942193
}
21952194

21962195
void emit();
@@ -2326,7 +2325,7 @@ void AssignIsolatedIntoSendingResultDiagnosticEmitter::emit() {
23262325
info.srcOperand,
23272326
diag::regionbasedisolation_out_sending_cannot_be_actor_isolated_named,
23282327
*varName, descriptiveKindStr)
2329-
.limitBehaviorIf(getBehaviorLimit());
2328+
.limitBehaviorIf(getConcurrencyDiagnosticBehavior());
23302329

23312330
diagnoseNote(
23322331
info.srcOperand,
@@ -2342,7 +2341,7 @@ void AssignIsolatedIntoSendingResultDiagnosticEmitter::emit() {
23422341
info.srcOperand,
23432342
diag::regionbasedisolation_out_sending_cannot_be_actor_isolated_type,
23442343
type, descriptiveKindStr)
2345-
.limitBehaviorIf(getBehaviorLimit());
2344+
.limitBehaviorIf(getConcurrencyDiagnosticBehavior());
23462345

23472346
diagnoseNote(
23482347
info.srcOperand,

lib/SILOptimizer/Utils/SILIsolationInfo.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,8 +1173,26 @@ bool SILIsolationInfo::isNonSendableType(SILType type, SILFunction *fn) {
11731173
return false;
11741174
}
11751175

1176-
// Otherwise, delegate to seeing if type conforms to the Sendable protocol.
1177-
return !type.isSendable(fn);
1176+
// First before we do anything, see if we have a Sendable type. In such a
1177+
// case, just return true early.
1178+
//
1179+
// DISCUSSION: It is important that we do this first since otherwise calling
1180+
// getConcurrencyDiagnosticBehavior could cause us to prevent a
1181+
// "preconcurrency" unneeded diagnostic when just using Sendable values. We
1182+
// only want to trigger that if we analyze a non-Sendable type.
1183+
if (type.isSendable(fn))
1184+
return false;
1185+
1186+
// Grab out behavior. If it is none, then we have a type that we want to treat
1187+
// as non-Sendable.
1188+
auto behavior = type.getConcurrencyDiagnosticBehavior(fn);
1189+
if (!behavior)
1190+
return true;
1191+
1192+
// Finally, if we are not supposed to ignore, then we have a true non-Sendable
1193+
// type. Types whose diagnostics we are supposed to ignore, we want to treat
1194+
// as Sendable.
1195+
return *behavior != DiagnosticBehavior::Ignore;
11781196
}
11791197

11801198
//===----------------------------------------------------------------------===//

test/Concurrency/sendable_preconcurrency.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,12 @@ struct MyType3 {
3131
}
3232

3333
func testA(ns: NS, mt: MyType, mt2: MyType2, mt3: MyType3, sc: StrictClass, nsc: NonStrictClass) async {
34-
// This is task isolated since we are capturing function arguments... but
35-
// since we are merging NonStrictClass from a preconcurrency module, the whole
36-
// error is squelched since we allow for preconcurrency to apply to the entire
37-
// region.
38-
Task {
34+
Task { // expected-tns-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}}
3935
print(ns)
4036
print(mt)
4137
print(mt2)
4238
print(mt3)
43-
print(sc)
39+
print(sc) // expected-tns-note {{closure captures 'sc' which is accessible to code in the current task}}
4440
print(nsc)
4541
}
4642
}

0 commit comments

Comments
 (0)