Skip to content

Commit eb78da2

Browse files
author
jturcotti
committed
modify Sendable checking of overrides and protocol conformances. In the past, both the results and parameters of overriding (resp. conforming) functions were checked for Sendability. This is overly restrictive. For safety, the parameters of the overridden (resp. requiring) function should be checked for Sendability and the results of the overriding (resp. conforming) should be checked. This commit implements that change.
1 parent 7092e50 commit eb78da2

File tree

7 files changed

+238
-63
lines changed

7 files changed

+238
-63
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5176,8 +5176,8 @@ WARNING(non_sendable_param_type,none,
51765176
"non-sendable type %0 %select{passed in call to %4 %2 %3|"
51775177
"exiting %4 context in call to non-isolated %2 %3|"
51785178
"passed in implicitly asynchronous call to %4 %2 %3|"
5179-
"in parameter of %4 %2 %3 satisfying protocol requirement|"
5180-
"in parameter of %4 overriding %2 %3|"
5179+
"in parameter of the protocol requirement satisfied by %4 %2 %3|"
5180+
"in parameter of superclass method overridden by %4 %2 %3|"
51815181
"in parameter of %4 '@objc' %2 %3}1 cannot cross actor boundary",
51825182
(Type, unsigned, DescriptiveDeclKind, DeclName, ActorIsolation))
51835183
WARNING(non_sendable_call_param_type,none,

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,8 +1003,9 @@ bool swift::diagnoseNonSendableTypes(
10031003
}
10041004

10051005
bool swift::diagnoseNonSendableTypesInReference(
1006-
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
1007-
SendableCheckReason reason, Optional<ActorIsolation> knownIsolation) {
1006+
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc refLoc,
1007+
SendableCheckReason refKind, Optional<ActorIsolation> knownIsolation,
1008+
FunctionCheckKind funcCheckKind, SourceLoc diagnoseLoc) {
10081009

10091010
// Retrieve the actor isolation to use in diagnostics.
10101011
auto getActorIsolation = [&] {
@@ -1017,23 +1018,31 @@ bool swift::diagnoseNonSendableTypesInReference(
10171018
// For functions, check the parameter and result types.
10181019
SubstitutionMap subs = declRef.getSubstitutions();
10191020
if (auto function = dyn_cast<AbstractFunctionDecl>(declRef.getDecl())) {
1020-
for (auto param : *function->getParameters()) {
1021-
Type paramType = param->getInterfaceType().subst(subs);
1022-
if (diagnoseNonSendableTypes(
1023-
paramType, fromDC, loc, diag::non_sendable_param_type,
1024-
(unsigned)reason, function->getDescriptiveKind(),
1025-
function->getName(), getActorIsolation()))
1026-
return true;
1021+
if (funcCheckKind != FunctionCheckKind::Results) {
1022+
// only check params if funcCheckKind specifies so
1023+
for (auto param : *function->getParameters()) {
1024+
Type paramType = param->getInterfaceType().subst(subs);
1025+
if (diagnoseNonSendableTypes(
1026+
paramType, fromDC, refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1027+
diag::non_sendable_param_type,
1028+
(unsigned)refKind, function->getDescriptiveKind(),
1029+
function->getName(), getActorIsolation()))
1030+
return true;
1031+
}
10271032
}
10281033

10291034
// Check the result type of a function.
10301035
if (auto func = dyn_cast<FuncDecl>(function)) {
1031-
Type resultType = func->getResultInterfaceType().subst(subs);
1032-
if (diagnoseNonSendableTypes(
1033-
resultType, fromDC, loc, diag::non_sendable_result_type,
1034-
(unsigned)reason, func->getDescriptiveKind(), func->getName(),
1035-
getActorIsolation()))
1036-
return true;
1036+
if (funcCheckKind != FunctionCheckKind::Params) {
1037+
// only check results if funcCheckKind specifies so
1038+
Type resultType = func->getResultInterfaceType().subst(subs);
1039+
if (diagnoseNonSendableTypes(
1040+
resultType, fromDC, refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1041+
diag::non_sendable_result_type,
1042+
(unsigned)refKind, func->getDescriptiveKind(), func->getName(),
1043+
getActorIsolation()))
1044+
return true;
1045+
}
10371046
}
10381047

10391048
return false;
@@ -1044,33 +1053,40 @@ bool swift::diagnoseNonSendableTypesInReference(
10441053
? var->getType()
10451054
: var->getValueInterfaceType().subst(subs);
10461055
if (diagnoseNonSendableTypes(
1047-
propertyType, fromDC, loc,
1056+
propertyType, fromDC, refLoc,
10481057
diag::non_sendable_property_type,
10491058
var->getDescriptiveKind(), var->getName(),
10501059
var->isLocalCapture(),
1051-
(unsigned)reason,
1060+
(unsigned)refKind,
10521061
getActorIsolation()))
10531062
return true;
10541063
}
10551064

10561065
if (auto subscript = dyn_cast<SubscriptDecl>(declRef.getDecl())) {
10571066
for (auto param : *subscript->getIndices()) {
1058-
Type paramType = param->getInterfaceType().subst(subs);
1067+
if (funcCheckKind != FunctionCheckKind::Results) {
1068+
// Check params of this subscript override for sendability
1069+
Type paramType = param->getInterfaceType().subst(subs);
1070+
if (diagnoseNonSendableTypes(
1071+
paramType, fromDC, refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1072+
diag::non_sendable_param_type,
1073+
(unsigned)refKind, subscript->getDescriptiveKind(),
1074+
subscript->getName(), getActorIsolation()))
1075+
return true;
1076+
}
1077+
}
1078+
1079+
if (funcCheckKind != FunctionCheckKind::Results) {
1080+
// Check the element type of a subscript.
1081+
Type resultType = subscript->getElementInterfaceType().subst(subs);
10591082
if (diagnoseNonSendableTypes(
1060-
paramType, fromDC, loc, diag::non_sendable_param_type,
1061-
(unsigned)reason, subscript->getDescriptiveKind(),
1062-
subscript->getName(), getActorIsolation()))
1083+
resultType, fromDC, refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1084+
diag::non_sendable_result_type,
1085+
(unsigned)refKind, subscript->getDescriptiveKind(),
1086+
subscript->getName(), getActorIsolation()))
10631087
return true;
10641088
}
10651089

1066-
// Check the element type of a subscript.
1067-
Type resultType = subscript->getElementInterfaceType().subst(subs);
1068-
if (diagnoseNonSendableTypes(
1069-
resultType, fromDC, loc, diag::non_sendable_result_type,
1070-
(unsigned)reason, subscript->getDescriptiveKind(),
1071-
subscript->getName(), getActorIsolation()))
1072-
return true;
1073-
10741090
return false;
10751091
}
10761092

@@ -3892,7 +3908,7 @@ static ActorIsolation getOverriddenIsolationFor(ValueDecl *value) {
38923908
return isolation.subst(subs);
38933909
}
38943910

3895-
static ConcreteDeclRef getDeclRefInContext(ValueDecl *value) {
3911+
ConcreteDeclRef swift::getDeclRefInContext(ValueDecl *value) {
38963912
auto declContext = value->getInnermostDeclContext();
38973913
if (auto genericEnv = declContext->getGenericEnvironmentOfContext()) {
38983914
return ConcreteDeclRef(
@@ -4368,9 +4384,18 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) {
43684384
return;
43694385

43704386
case OverrideIsolationResult::Sendable:
4387+
// Check that the results of the overriding method are sendable
43714388
diagnoseNonSendableTypesInReference(
43724389
getDeclRefInContext(value), value->getInnermostDeclContext(),
4373-
value->getLoc(), SendableCheckReason::Override);
4390+
value->getLoc(), SendableCheckReason::Override,
4391+
getActorIsolation(value), FunctionCheckKind::Results);
4392+
4393+
// Check that the parameters of the overridden method are sendable
4394+
diagnoseNonSendableTypesInReference(
4395+
getDeclRefInContext(overridden), overridden->getInnermostDeclContext(),
4396+
overridden->getLoc(), SendableCheckReason::Override,
4397+
getActorIsolation(value), FunctionCheckKind::Params,
4398+
value->getLoc());
43744399
return;
43754400

43764401
case OverrideIsolationResult::Disallowed:

lib/Sema/TypeCheckConcurrency.h

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,17 @@ struct ActorReferenceResult {
246246
operator Kind() const { return kind; }
247247
};
248248

249+
/// Specifies whether checks applied to function types should
250+
/// apply to their params, results, or both
251+
enum class FunctionCheckKind {
252+
/// Check params and results
253+
ParamsResults,
254+
/// Check params only
255+
Params,
256+
/// Check results only
257+
Results,
258+
};
259+
249260
/// Diagnose the presence of any non-sendable types when referencing a
250261
/// given declaration from a particular declaration context.
251262
///
@@ -260,17 +271,26 @@ struct ActorReferenceResult {
260271
///
261272
/// \param fromDC The context from which the reference occurs.
262273
///
263-
/// \param loc The location at which the reference occurs, which will be
274+
/// \param refLoc The location at which the reference occurs, which will be
264275
/// used when emitting diagnostics.
265276
///
266277
/// \param refKind Describes what kind of reference is being made, which is
267278
/// used to tailor the diagnostic.
268279
///
280+
/// \param funcCheckKind Describes whether function types in this reference
281+
/// should be checked for sendability of their results, params, or both
282+
///
283+
/// \param diagnoseLoc Provides an alternative source location to `refLoc`
284+
/// to be used for reporting the top level diagnostic while auxiliary
285+
/// warnings and diagnostics are reported at `refLoc`.
286+
///
269287
/// \returns true if an problem was detected, false otherwise.
270288
bool diagnoseNonSendableTypesInReference(
271-
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
289+
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc refLoc,
272290
SendableCheckReason refKind,
273-
Optional<ActorIsolation> knownIsolation = None);
291+
Optional<ActorIsolation> knownIsolation = None,
292+
FunctionCheckKind funcCheckKind = FunctionCheckKind::ParamsResults,
293+
SourceLoc diagnoseLoc = SourceLoc());
274294

275295
/// Produce a diagnostic for a missing conformance to Sendable.
276296
void diagnoseMissingSendableConformance(
@@ -283,6 +303,9 @@ void diagnoseMissingExplicitSendable(NominalTypeDecl *nominal);
283303
/// Warn about deprecated `Executor.enqueue` implementations.
284304
void tryDiagnoseExecutorConformance(ASTContext &C, const NominalTypeDecl *nominal, ProtocolDecl *proto);
285305

306+
// Get a concrete reference to a declaration
307+
ConcreteDeclRef getDeclRefInContext(ValueDecl *value);
308+
286309
/// How the Sendable check should be performed.
287310
enum class SendableCheck {
288311
/// Sendable conformance was explicitly stated and should be
@@ -359,6 +382,37 @@ namespace detail {
359382
};
360383
}
361384

385+
/// Diagnose any non-Sendable types that occur within the given type, using
386+
/// the given diagnostic.
387+
///
388+
/// \param typeLoc is the source location of the type being diagnosed
389+
///
390+
/// \param diagnoseLoc is the source location at which the main diagnostic should
391+
/// be reported, which can differ from typeLoc
392+
///
393+
/// \returns \c true if any errors were produced, \c false if no diagnostics or
394+
/// only warnings and notes were produced.
395+
template<typename ...DiagArgs>
396+
bool diagnoseNonSendableTypes(
397+
Type type, SendableCheckContext fromContext,
398+
SourceLoc typeLoc, SourceLoc diagnoseLoc,
399+
Diag<Type, DiagArgs...> diag,
400+
typename detail::Identity<DiagArgs>::type ...diagArgs) {
401+
402+
ASTContext &ctx = fromContext.fromDC->getASTContext();
403+
return diagnoseNonSendableTypes(
404+
type, fromContext, typeLoc, [&](Type specificType,
405+
DiagnosticBehavior behavior) {
406+
407+
if (behavior != DiagnosticBehavior::Ignore) {
408+
ctx.Diags.diagnose(diagnoseLoc, diag, type, diagArgs...)
409+
.limitBehavior(behavior);
410+
}
411+
412+
return false;
413+
});
414+
}
415+
362416
/// Diagnose any non-Sendable types that occur within the given type, using
363417
/// the given diagnostic.
364418
///
@@ -369,17 +423,9 @@ bool diagnoseNonSendableTypes(
369423
Type type, SendableCheckContext fromContext, SourceLoc loc,
370424
Diag<Type, DiagArgs...> diag,
371425
typename detail::Identity<DiagArgs>::type ...diagArgs) {
372-
ASTContext &ctx = fromContext.fromDC->getASTContext();
373-
return diagnoseNonSendableTypes(
374-
type, fromContext, loc, [&](Type specificType,
375-
DiagnosticBehavior behavior) {
376-
if (behavior != DiagnosticBehavior::Ignore) {
377-
ctx.Diags.diagnose(loc, diag, type, diagArgs...)
378-
.limitBehavior(behavior);
379-
}
380426

381-
return false;
382-
});
427+
return diagnoseNonSendableTypes(type, fromContext, loc, loc, diag,
428+
std::forward<decltype(diagArgs)>(diagArgs)...);
383429
}
384430

385431
/// Diagnose this sendability error with behavior based on the import of

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3006,16 +3006,6 @@ static bool hasExplicitGlobalActorAttr(ValueDecl *decl) {
30063006

30073007
Optional<ActorIsolation> ConformanceChecker::checkActorIsolation(
30083008
ValueDecl *requirement, ValueDecl *witness) {
3009-
/// Retrieve a concrete witness for Sendable checking.
3010-
auto getConcreteWitness = [&] {
3011-
if (auto genericEnv = witness->getInnermostDeclContext()
3012-
->getGenericEnvironmentOfContext()) {
3013-
return ConcreteDeclRef(
3014-
witness, genericEnv->getForwardingSubstitutionMap());
3015-
}
3016-
3017-
return ConcreteDeclRef(witness);
3018-
};
30193009

30203010
// Determine the isolation of the requirement itself.
30213011
auto requirementIsolation = getActorIsolation(requirement);
@@ -3031,7 +3021,7 @@ Optional<ActorIsolation> ConformanceChecker::checkActorIsolation(
30313021
loc = Conformance->getLoc();
30323022

30333023
auto refResult = ActorReferenceResult::forReference(
3034-
getConcreteWitness(), witness->getLoc(), DC, None, None,
3024+
getDeclRefInContext(witness), witness->getLoc(), DC, None, None,
30353025
None, requirementIsolation);
30363026
bool sameConcurrencyDomain = false;
30373027
switch (refResult) {
@@ -3048,7 +3038,7 @@ Optional<ActorIsolation> ConformanceChecker::checkActorIsolation(
30483038

30493039
case ActorReferenceResult::ExitsActorToNonisolated:
30503040
diagnoseNonSendableTypesInReference(
3051-
getConcreteWitness(), DC, loc, SendableCheckReason::Conformance);
3041+
getDeclRefInContext(witness), DC, loc, SendableCheckReason::Conformance);
30523042
return None;
30533043

30543044
case ActorReferenceResult::EntersActor:
@@ -3130,8 +3120,19 @@ Optional<ActorIsolation> ConformanceChecker::checkActorIsolation(
31303120
witness->getAttrs().hasAttribute<NonisolatedAttr>())
31313121
return None;
31323122

3123+
// Check that the results of the witnessing method are sendable
31333124
diagnoseNonSendableTypesInReference(
3134-
getConcreteWitness(), DC, loc, SendableCheckReason::Conformance);
3125+
getDeclRefInContext(witness), DC, loc, SendableCheckReason::Conformance,
3126+
getActorIsolation(witness), FunctionCheckKind::Results);
3127+
3128+
// If this requirement is a function, check that its parameters are Sendable as well
3129+
if (isa<AbstractFunctionDecl>(requirement)) {
3130+
diagnoseNonSendableTypesInReference(
3131+
getDeclRefInContext(requirement),
3132+
requirement->getInnermostDeclContext(), requirement->getLoc(),
3133+
SendableCheckReason::Conformance, getActorIsolation(witness),
3134+
FunctionCheckKind::Params, loc);
3135+
}
31353136

31363137
// If the witness is accessible across actors, we don't need to consider it
31373138
// isolated.

test/Concurrency/concurrent_value_checking.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ protocol AsyncProto {
223223
}
224224

225225
extension A1: AsyncProto {
226-
func asyncMethod(_: NotConcurrent) async { } // expected-warning{{non-sendable type 'NotConcurrent' in parameter of actor-isolated instance method 'asyncMethod' satisfying protocol requirement cannot cross actor boundary}}
226+
func asyncMethod(_: NotConcurrent) async { } // expected-warning{{non-sendable type 'NotConcurrent' in parameter of the protocol requirement satisfied by actor-isolated instance method 'asyncMethod' cannot cross actor boundary}}
227227
}
228228

229229
protocol MainActorProto {
@@ -232,7 +232,7 @@ protocol MainActorProto {
232232

233233
class SomeClass: MainActorProto {
234234
@SomeGlobalActor
235-
func asyncMainMethod(_: NotConcurrent) async { } // expected-warning{{non-sendable type 'NotConcurrent' in parameter of global actor 'SomeGlobalActor'-isolated instance method 'asyncMainMethod' satisfying protocol requirement cannot cross actor boundary}}
235+
func asyncMainMethod(_: NotConcurrent) async { } // expected-warning{{non-sendable type 'NotConcurrent' in parameter of the protocol requirement satisfied by global actor 'SomeGlobalActor'-isolated instance method 'asyncMainMethod' cannot cross actor boundary}}
236236
}
237237

238238
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)