Skip to content

Commit 469099f

Browse files
authored
Merge pull request #40617 from DougGregor/sendable-isolated-witnesses-5.6
2 parents f878282 + cc1ed15 commit 469099f

12 files changed

+324
-85
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4573,17 +4573,36 @@ NOTE(protocol_isolated_to_global_actor_here,none,
45734573

45744574
ERROR(isolated_parameter_not_actor,none,
45754575
"'isolated' parameter has non-actor type %0", (Type))
4576-
4576+
45774577
WARNING(non_sendable_param_type,none,
4578-
"cannot pass argument of non-sendable type %0 across actors",
4579-
(Type))
4578+
"non-sendable type %0 %select{passed in call to %4 %2 %3|"
4579+
"passed in implicitly asynchronous call to %4 %2 %3|"
4580+
"in parameter of %4 %2 %3 satisfying non-isolated protocol "
4581+
"requirement|"
4582+
"in parameter of %4 '@objc' %2 %3}1 cannot cross actor boundary",
4583+
(Type, unsigned, DescriptiveDeclKind, DeclName, ActorIsolation))
4584+
WARNING(non_sendable_call_param_type,none,
4585+
"non-sendable type %0 passed in %select{implicitly asynchronous |}1"
4586+
"call to %2 function cannot cross actor boundary",
4587+
(Type, bool, ActorIsolation))
45804588
WARNING(non_sendable_result_type,none,
4581-
"cannot call function returning non-sendable type %0 across "
4582-
"actors", (Type))
4589+
"non-sendable type %0 returned by %select{call to %4 %2 %3|"
4590+
"implicitly asynchronous call to %4 %2 %3|"
4591+
"%4 %2 %3 satisfying non-isolated protocol requirement|"
4592+
"%4 '@objc' %2 %3}1 cannot cross actor boundary",
4593+
(Type, unsigned, DescriptiveDeclKind, DeclName, ActorIsolation))
4594+
WARNING(non_sendable_call_result_type,none,
4595+
"non-sendable type %0 returned by %select{implicitly asynchronous |}1"
4596+
"call to %2 function cannot cross actor boundary",
4597+
(Type, bool, ActorIsolation))
45834598
WARNING(non_sendable_property_type,none,
4584-
"cannot use %1 %2 with a non-sendable type %0 "
4585-
"%select{across actors|from concurrently-executed code}3",
4586-
(Type, DescriptiveDeclKind, DeclName, bool))
4599+
"non-sendable type %0 in %select{"
4600+
"%select{asynchronous access to %5 %1 %2|"
4601+
"implicitly asynchronous access to %5 %1 %2|"
4602+
"conformance of %5 %1 %2 to non-isolated protocol requirement|"
4603+
"%5 '@objc' %1 %2}4|captured local %1 %2}3 cannot "
4604+
"cross %select{actor|task}3 boundary",
4605+
(Type, DescriptiveDeclKind, DeclName, bool, unsigned, ActorIsolation))
45874606
WARNING(non_sendable_keypath_capture,none,
45884607
"cannot form key path that captures non-sendable type %0",
45894608
(Type))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -781,22 +781,26 @@ bool swift::diagnoseNonSendableTypes(
781781

782782
bool swift::diagnoseNonSendableTypesInReference(
783783
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
784-
ConcurrentReferenceKind refKind) {
784+
SendableCheckReason reason) {
785785
// For functions, check the parameter and result types.
786786
SubstitutionMap subs = declRef.getSubstitutions();
787787
if (auto function = dyn_cast<AbstractFunctionDecl>(declRef.getDecl())) {
788788
for (auto param : *function->getParameters()) {
789789
Type paramType = param->getInterfaceType().subst(subs);
790790
if (diagnoseNonSendableTypes(
791-
paramType, fromDC, loc, diag::non_sendable_param_type))
791+
paramType, fromDC, loc, diag::non_sendable_param_type,
792+
(unsigned)reason, function->getDescriptiveKind(),
793+
function->getName(), getActorIsolation(function)))
792794
return true;
793795
}
794796

795797
// Check the result type of a function.
796798
if (auto func = dyn_cast<FuncDecl>(function)) {
797799
Type resultType = func->getResultInterfaceType().subst(subs);
798800
if (diagnoseNonSendableTypes(
799-
resultType, fromDC, loc, diag::non_sendable_result_type))
801+
resultType, fromDC, loc, diag::non_sendable_result_type,
802+
(unsigned)reason, func->getDescriptiveKind(), func->getName(),
803+
getActorIsolation(func)))
800804
return true;
801805
}
802806

@@ -811,22 +815,28 @@ bool swift::diagnoseNonSendableTypesInReference(
811815
propertyType, fromDC, loc,
812816
diag::non_sendable_property_type,
813817
var->getDescriptiveKind(), var->getName(),
814-
var->isLocalCapture()))
818+
var->isLocalCapture(),
819+
(unsigned)reason,
820+
getActorIsolation(var)))
815821
return true;
816822
}
817823

818824
if (auto subscript = dyn_cast<SubscriptDecl>(declRef.getDecl())) {
819825
for (auto param : *subscript->getIndices()) {
820826
Type paramType = param->getInterfaceType().subst(subs);
821827
if (diagnoseNonSendableTypes(
822-
paramType, fromDC, loc, diag::non_sendable_param_type))
828+
paramType, fromDC, loc, diag::non_sendable_param_type,
829+
(unsigned)reason, subscript->getDescriptiveKind(),
830+
subscript->getName(), getActorIsolation(subscript)))
823831
return true;
824832
}
825833

826834
// Check the element type of a subscript.
827835
Type resultType = subscript->getElementInterfaceType().subst(subs);
828836
if (diagnoseNonSendableTypes(
829-
resultType, fromDC, loc, diag::non_sendable_result_type))
837+
resultType, fromDC, loc, diag::non_sendable_result_type,
838+
(unsigned)reason, subscript->getDescriptiveKind(),
839+
subscript->getName(), getActorIsolation(subscript)))
830840
return true;
831841

832842
return false;
@@ -1973,12 +1983,11 @@ namespace {
19731983
}
19741984

19751985
if (result == AsyncMarkingResult::FoundAsync) {
1976-
19771986
// Check for non-sendable types.
19781987
bool problemFound =
19791988
diagnoseNonSendableTypesInReference(
19801989
concDeclRef, getDeclContext(), declLoc,
1981-
ConcurrentReferenceKind::SynchronousAsAsyncCall);
1990+
SendableCheckReason::SynchronousAsAsync);
19821991
if (problemFound)
19831992
result = AsyncMarkingResult::NotSendable;
19841993
}
@@ -2104,18 +2113,32 @@ namespace {
21042113
}
21052114

21062115
// Check for sendability of the parameter types.
2107-
for (const auto &param : fnType->getParams()) {
2108-
// FIXME: Dig out the locations of the corresponding arguments.
2116+
auto params = fnType->getParams();
2117+
for (unsigned paramIdx : indices(params)) {
2118+
const auto &param = params[paramIdx];
2119+
2120+
// Dig out the location of the argument.
2121+
SourceLoc argLoc = apply->getLoc();
2122+
if (auto argList = apply->getArgs()) {
2123+
auto arg = argList->get(paramIdx);
2124+
if (arg.getStartLoc().isValid())
2125+
argLoc = arg.getStartLoc();
2126+
}
2127+
21092128
if (diagnoseNonSendableTypes(
2110-
param.getParameterType(), getDeclContext(), apply->getLoc(),
2111-
diag::non_sendable_param_type))
2129+
param.getParameterType(), getDeclContext(), argLoc,
2130+
diag::non_sendable_call_param_type,
2131+
apply->isImplicitlyAsync().hasValue(),
2132+
*unsatisfiedIsolation))
21122133
return true;
21132134
}
21142135

21152136
// Check for sendability of the result type.
21162137
if (diagnoseNonSendableTypes(
21172138
fnType->getResult(), getDeclContext(), apply->getLoc(),
2118-
diag::non_sendable_result_type))
2139+
diag::non_sendable_call_result_type,
2140+
apply->isImplicitlyAsync().hasValue(),
2141+
*unsatisfiedIsolation))
21192142
return true;
21202143

21212144
return false;
@@ -2141,7 +2164,7 @@ namespace {
21412164
if (isCrossActor) {
21422165
return diagnoseNonSendableTypesInReference(
21432166
valueRef, getDeclContext(), loc,
2144-
ConcurrentReferenceKind::CrossActor);
2167+
SendableCheckReason::CrossActor);
21452168
}
21462169

21472170
// Call is implicitly asynchronous.
@@ -2482,7 +2505,7 @@ namespace {
24822505

24832506
return diagnoseNonSendableTypesInReference(
24842507
memberRef, getDeclContext(), memberLoc,
2485-
ConcurrentReferenceKind::CrossActor);
2508+
SendableCheckReason::CrossActor);
24862509
}
24872510

24882511
case ActorIsolationRestriction::ActorSelf: {
@@ -3266,6 +3289,13 @@ ActorIsolation ActorIsolationRequest::evaluate(
32663289
ASTContext &ctx = value->getASTContext();
32673290
switch (inferred) {
32683291
case ActorIsolation::Independent:
3292+
// Stored properties cannot be non-isolated, so don't infer it.
3293+
if (auto var = dyn_cast<VarDecl>(value)) {
3294+
if (!var->isStatic() && var->hasStorage())
3295+
return ActorIsolation::forUnspecified();
3296+
}
3297+
3298+
32693299
if (onlyGlobal)
32703300
return ActorIsolation::forUnspecified();
32713301

lib/Sema/TypeCheckConcurrency.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,21 @@ void checkPropertyWrapperActorIsolation(VarDecl *wrappedVar, Expr *expr);
7070
ClosureActorIsolation
7171
determineClosureActorIsolation(AbstractClosureExpr *closure);
7272

73-
/// Describes the kind of operation that introduced the concurrent refernece.
74-
enum class ConcurrentReferenceKind {
75-
/// A synchronous operation that was "promoted" to an asynchronous call
76-
/// because it was out of the actor's domain.
77-
SynchronousAsAsyncCall,
78-
/// A cross-actor reference.
73+
/// States the reason for checking the Sendability of a given declaration.
74+
enum class SendableCheckReason {
75+
/// A reference to an actor from outside that actor.
7976
CrossActor,
80-
/// A local capture referenced from concurrent code.
81-
LocalCapture,
82-
/// Concurrent function
83-
ConcurrentFunction,
84-
/// Nonisolated declaration.
85-
Nonisolated,
77+
78+
/// A synchronous operation that was "promoted" to an asynchronous one
79+
/// because it was out of the actor's domain.
80+
SynchronousAsAsync,
81+
82+
/// A protocol conformance where the witness/requirement have different
83+
/// actor isolation.
84+
Conformance,
85+
86+
/// The declaration is being exposed to Objective-C.
87+
ObjC,
8688
};
8789

8890
/// The isolation restriction in effect for a given declaration that is
@@ -241,7 +243,7 @@ bool contextRequiresStrictConcurrencyChecking(
241243
/// \returns true if an problem was detected, false otherwise.
242244
bool diagnoseNonSendableTypesInReference(
243245
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
244-
ConcurrentReferenceKind refKind);
246+
SendableCheckReason refKind);
245247

246248
/// Produce a diagnostic for a missing conformance to Sendable.
247249
void diagnoseMissingSendableConformance(

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ static bool checkObjCActorIsolation(const ValueDecl *VD,
439439
// FIXME: Substitution map?
440440
diagnoseNonSendableTypesInReference(
441441
const_cast<ValueDecl *>(VD), VD->getDeclContext(),
442-
VD->getLoc(), ConcurrentReferenceKind::CrossActor);
442+
VD->getLoc(), SendableCheckReason::ObjC);
443443
return false;
444444
case ActorIsolationRestriction::ActorSelf:
445445
// Actor-isolated functions cannot be @objc.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,6 +2862,31 @@ static void emitDeclaredHereIfNeeded(DiagnosticEngine &diags,
28622862
diags.diagnose(value, diag::decl_declared_here, value->getName());
28632863
}
28642864

2865+
/// Determine if the witness can be made implicitly async.
2866+
static bool isValidImplicitAsync(ValueDecl *witness, ValueDecl *requirement) {
2867+
if (auto witnessFunc = dyn_cast<AbstractFunctionDecl>(witness)) {
2868+
if (auto requirementFunc = dyn_cast<AbstractFunctionDecl>(requirement)) {
2869+
return requirementFunc->hasAsync() &&
2870+
!(witnessFunc->hasThrows() && !requirementFunc->hasThrows());
2871+
}
2872+
2873+
return false;
2874+
}
2875+
2876+
if (auto witnessStorage = dyn_cast<AbstractStorageDecl>(witness)) {
2877+
if (auto requirementStorage = dyn_cast<AbstractStorageDecl>(requirement)) {
2878+
auto requirementAccessor = requirementStorage->getEffectfulGetAccessor();
2879+
if (!requirementAccessor || !requirementAccessor->hasAsync())
2880+
return false;
2881+
2882+
return witnessStorage->isLessEffectfulThan(
2883+
requirementStorage, EffectKind::Throws);
2884+
}
2885+
}
2886+
2887+
return false;
2888+
}
2889+
28652890
bool ConformanceChecker::checkActorIsolation(
28662891
ValueDecl *requirement, ValueDecl *witness) {
28672892
/// Retrieve a concrete witness for Sendable checking.
@@ -2986,9 +3011,15 @@ bool ConformanceChecker::checkActorIsolation(
29863011
// A synchronous actor function can witness an asynchronous protocol
29873012
// requirement, since calls "through" the protocol are always cross-actor,
29883013
// in which case the function becomes implicitly async.
3014+
//
3015+
// In this case, we're crossing actor boundaries so we need to
3016+
// perform Sendable checking.
29893017
if (witnessClass && witnessClass->isActor()) {
2990-
if (requirementFunc && requirementFunc->hasAsync() &&
2991-
(requirementFunc->hasThrows() == witnessFunc->hasThrows())) {
3018+
if (isValidImplicitAsync(witness, requirement)) {
3019+
diagnoseNonSendableTypesInReference(
3020+
getConcreteWitness(), DC, witness->getLoc(),
3021+
SendableCheckReason::Conformance);
3022+
29923023
return false;
29933024
}
29943025
}
@@ -3026,7 +3057,7 @@ bool ConformanceChecker::checkActorIsolation(
30263057
case ActorIsolationRestriction::CrossActorSelf: {
30273058
if (diagnoseNonSendableTypesInReference(
30283059
getConcreteWitness(), DC, witness->getLoc(),
3029-
ConcurrentReferenceKind::CrossActor)) {
3060+
SendableCheckReason::Conformance)) {
30303061
return true;
30313062
}
30323063

@@ -3157,7 +3188,7 @@ bool ConformanceChecker::checkActorIsolation(
31573188

31583189
return diagnoseNonSendableTypesInReference(
31593190
getConcreteWitness(), DC, witness->getLoc(),
3160-
ConcurrentReferenceKind::CrossActor);
3191+
SendableCheckReason::Conformance);
31613192
}
31623193

31633194
// If the witness has a global actor but the requirement does not, we have

0 commit comments

Comments
 (0)