Skip to content

Commit f15ef1a

Browse files
committed
[Concurrency] Allow isolated parameters to have optional type.
1 parent 33c7326 commit f15ef1a

File tree

5 files changed

+85
-6
lines changed

5 files changed

+85
-6
lines changed

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ EXPERIMENTAL_FEATURE(Extern, true)
254254
// global functions and key paths.
255255
EXPERIMENTAL_FEATURE(InferSendableFromCaptures, false)
256256

257+
// Allow optional isolated parameters.
258+
EXPERIMENTAL_FEATURE(OptionalIsolatedParameters, true)
259+
257260
/// Enable the `@_staticExclusiveOnly` attribute.
258261
EXPERIMENTAL_FEATURE(StaticExclusiveOnly, true)
259262

lib/AST/ASTPrinter.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3829,6 +3829,25 @@ static bool usesFeatureInferSendableFromCaptures(Decl *decl) {
38293829
return false;
38303830
}
38313831

3832+
static bool usesFeatureOptionalIsolatedParameters(Decl *decl) {
3833+
auto *value = dyn_cast<ValueDecl>(decl);
3834+
if (!value)
3835+
return false;
3836+
3837+
auto *paramList = getParameterList(value);
3838+
if (!paramList)
3839+
return false;
3840+
3841+
for (auto param : *paramList) {
3842+
if (param->isIsolated()) {
3843+
auto paramType = param->getInterfaceType();
3844+
return !paramType->getOptionalObjectType().isNull();
3845+
}
3846+
}
3847+
3848+
return false;
3849+
}
3850+
38323851
static bool usesFeaturePlaygroundExtendedCallbacks(Decl *decl) {
38333852
return false;
38343853
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3289,12 +3289,17 @@ namespace {
32893289
KnownProtocolKind::Actor);
32903290
}
32913291

3292-
unsatisfiedIsolation =
3293-
ActorIsolation::forActorInstanceParameter(nominal, paramIdx);
3292+
// FIXME: Also allow 'Optional.none'
3293+
if (dyn_cast<NilLiteralExpr>(arg->findOriginalValue())) {
3294+
mayExitToNonisolated = true;
3295+
} else {
3296+
unsatisfiedIsolation =
3297+
ActorIsolation::forActorInstanceParameter(nominal, paramIdx);
3298+
mayExitToNonisolated = false;
3299+
}
32943300

32953301
if (!fnType->getExtInfo().isAsync())
32963302
callOptions |= ActorReferenceResult::Flags::AsyncPromotion;
3297-
mayExitToNonisolated = false;
32983303

32993304
break;
33003305
}
@@ -4613,7 +4618,13 @@ ActorIsolation ActorIsolationRequest::evaluate(
46134618
}
46144619
}
46154620

4616-
if (auto actor = paramType->getAnyActor())
4621+
Type actorType;
4622+
if (auto wrapped = paramType->getOptionalObjectType()) {
4623+
actorType = wrapped;
4624+
} else {
4625+
actorType = paramType;
4626+
}
4627+
if (auto actor = actorType->getAnyActor())
46174628
return ActorIsolation::forActorInstanceParameter(actor, *paramIdx);
46184629
}
46194630

lib/Sema/TypeCheckType.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4485,6 +4485,14 @@ TypeResolver::resolveIsolatedTypeRepr(IsolatedTypeRepr *repr,
44854485

44864486
// isolated parameters must be of actor type
44874487
if (!type->hasTypeParameter() && !type->isActorType() && !type->hasError()) {
4488+
// Optional actor types are fine - `nil` represents `nonisolated`.
4489+
auto wrapped = type->getOptionalObjectType();
4490+
auto allowOptional = getASTContext().LangOpts
4491+
.hasFeature(Feature::OptionalIsolatedParameters);
4492+
if (allowOptional && wrapped && wrapped->isActorType()) {
4493+
return type;
4494+
}
4495+
44884496
diagnoseInvalid(
44894497
repr, repr->getSpecifierLoc(), diag::isolated_parameter_not_actor, type);
44904498
return ErrorType::get(type);

test/Concurrency/isolated_parameters.swift

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %target-swift-frontend -disable-availability-checking -strict-concurrency=complete %s -emit-sil -o /dev/null -verify -verify-additional-prefix complete-
2-
// RUN: %target-swift-frontend -disable-availability-checking -strict-concurrency=complete %s -emit-sil -o /dev/null -verify -enable-experimental-feature RegionBasedIsolation
1+
// RUN: %target-swift-frontend -disable-availability-checking -strict-concurrency=complete -enable-experimental-feature OptionalIsolatedParameters %s -emit-sil -o /dev/null -verify -verify-additional-prefix complete-
2+
// RUN: %target-swift-frontend -disable-availability-checking -strict-concurrency=complete -enable-experimental-feature OptionalIsolatedParameters %s -emit-sil -o /dev/null -verify -enable-experimental-feature RegionBasedIsolation
33

44
// REQUIRES: concurrency
55
// REQUIRES: asserts
@@ -357,3 +357,41 @@ func isolated_generic_bad_3<T: AnyActor>(_ t: isolated T) {}
357357
// expected-error@-1 {{'isolated' parameter 'T' must conform to 'Actor' or 'DistributedActor' protocol}}
358358

359359
func isolated_generic_ok_1<T: Actor>(_ t: isolated T) {}
360+
361+
class NotSendable {} // expected-complete-note 5 {{class 'NotSendable' does not conform to the 'Sendable' protocol}}
362+
363+
func optionalIsolated(_ ns: NotSendable, to actor: isolated (any Actor)?) async {}
364+
func optionalIsolatedSync(_ ns: NotSendable, to actor: isolated (any Actor)?) {}
365+
366+
nonisolated func callFromNonisolated(ns: NotSendable) async {
367+
await optionalIsolated(ns, to: nil)
368+
369+
optionalIsolatedSync(ns, to: nil)
370+
371+
let myActor = A()
372+
373+
await optionalIsolated(ns, to: myActor)
374+
// expected-complete-warning@-1 {{passing argument of non-sendable type 'NotSendable' into actor-isolated context may introduce data races}}
375+
376+
optionalIsolatedSync(ns, to: myActor)
377+
// expected-error@-1 {{expression is 'async' but is not marked with 'await'}}
378+
// expected-note@-2 {{calls to global function 'optionalIsolatedSync(_:to:)' from outside of its actor context are implicitly asynchronous}}
379+
// expected-complete-warning@-3 {{passing argument of non-sendable type 'NotSendable' into actor-isolated context may introduce data races}}
380+
}
381+
382+
@MainActor func callFromMainActor(ns: NotSendable) async {
383+
await optionalIsolated(ns, to: nil)
384+
// expected-complete-warning@-1 {{passing argument of non-sendable type 'NotSendable' outside of main actor-isolated context may introduce data races}}
385+
386+
optionalIsolatedSync(ns, to: nil)
387+
388+
let myActor = A()
389+
390+
await optionalIsolated(ns, to: myActor)
391+
// expected-complete-warning@-1 {{passing argument of non-sendable type 'NotSendable' into actor-isolated context may introduce data races}}
392+
393+
optionalIsolatedSync(ns, to: myActor)
394+
// expected-error@-1 {{expression is 'async' but is not marked with 'await'}}
395+
// expected-note@-2 {{calls to global function 'optionalIsolatedSync(_:to:)' from outside of its actor context are implicitly asynchronous}}
396+
// expected-complete-warning@-3 {{passing argument of non-sendable type 'NotSendable' into actor-isolated context may introduce data races}}
397+
}

0 commit comments

Comments
 (0)