Skip to content

Commit 3ef18ea

Browse files
authored
Merge pull request #79300 from gottesmm/pr-12bede947a34719b34b1aa8ea6c126a6126d6dd6
[concurrency] Implement serialization for execution(caller)/execution(concurrent).
2 parents 248d1f3 + 1fc836c commit 3ef18ea

8 files changed

+442
-110
lines changed

lib/SIL/IR/SILFunctionType.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -1660,9 +1660,7 @@ class DestructureInputs {
16601660
// If we are an async function that is unspecified or nonisolated, insert an
16611661
// isolated parameter if NonIsolatedAsyncInheritsIsolationFromContext is
16621662
// enabled.
1663-
if (TC.Context.LangOpts.hasFeature(
1664-
Feature::NonIsolatedAsyncInheritsIsolationFromContext) &&
1665-
IsolationInfo &&
1663+
if (IsolationInfo &&
16661664
IsolationInfo->getKind() == ActorIsolation::CallerIsolationInheriting &&
16671665
extInfoBuilder.isAsync()) {
16681666
auto actorProtocol = TC.Context.getProtocol(KnownProtocolKind::Actor);

lib/SILGen/SILGenConcurrency.cpp

+11-10
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,18 @@ setExpectedExecutorForParameterIsolation(SILGenFunction &SGF,
7474
// If we have caller isolation inheriting... just grab from our isolated
7575
// argument.
7676
if (actorIsolation.getKind() == ActorIsolation::CallerIsolationInheriting) {
77-
if (auto *isolatedArg = SGF.F.maybeGetIsolatedArgument()) {
78-
ManagedValue isolatedMV;
79-
if (isolatedArg->getOwnershipKind() == OwnershipKind::Guaranteed) {
80-
isolatedMV = ManagedValue::forBorrowedRValue(isolatedArg);
81-
} else {
82-
isolatedMV = ManagedValue::forUnmanagedOwnedValue(isolatedArg);
83-
}
84-
85-
SGF.ExpectedExecutor.set(SGF.emitLoadActorExecutor(loc, isolatedMV));
86-
return;
77+
auto *isolatedArg = SGF.F.maybeGetIsolatedArgument();
78+
assert(isolatedArg &&
79+
"Caller Isolation Inheriting without isolated parameter");
80+
ManagedValue isolatedMV;
81+
if (isolatedArg->getOwnershipKind() == OwnershipKind::Guaranteed) {
82+
isolatedMV = ManagedValue::forBorrowedRValue(isolatedArg);
83+
} else {
84+
isolatedMV = ManagedValue::forUnmanagedOwnedValue(isolatedArg);
8785
}
86+
87+
SGF.ExpectedExecutor.set(SGF.emitLoadActorExecutor(loc, isolatedMV));
88+
return;
8889
}
8990

9091
llvm_unreachable("Unhandled case?!");

lib/Sema/TypeCheckAttr.cpp

-10
Original file line numberDiff line numberDiff line change
@@ -265,16 +265,6 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
265265

266266
switch (attr->getBehavior()) {
267267
case ExecutionKind::Concurrent: {
268-
// 'concurrent' doesn't work with explicit `nonisolated`
269-
if (F->hasExplicitIsolationAttribute()) {
270-
if (F->getAttrs().hasAttribute<NonisolatedAttr>()) {
271-
diagnoseAndRemoveAttr(
272-
attr,
273-
diag::attr_execution_concurrent_incompatible_with_nonisolated, F);
274-
return;
275-
}
276-
}
277-
278268
auto parameters = F->getParameters();
279269
if (!parameters)
280270
return;

lib/Sema/TypeCheckConcurrency.cpp

+135-86
Original file line numberDiff line numberDiff line change
@@ -4753,6 +4753,24 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true,
47534753
return std::nullopt;
47544754
}
47554755

4756+
// If the declaration is explicitly marked with 'execution', return the
4757+
// appropriate isolation.
4758+
//
4759+
// NOTE: This needs to occur before we handle an explicit nonisolated attr,
4760+
// since if @execution and nonisolated are used together, we want to ensure
4761+
// that @execution takes priority. This ensures that if we import code from a
4762+
// module that was compiled with a different value for
4763+
// NonIsolatedAsyncInheritsIsolationFromContext, we get the semantics of the
4764+
// source module.
4765+
if (concurrentExecutionAttr) {
4766+
switch (concurrentExecutionAttr->getBehavior()) {
4767+
case ExecutionKind::Concurrent:
4768+
return ActorIsolation::forNonisolated(false /*is unsafe*/);
4769+
case ExecutionKind::Caller:
4770+
return ActorIsolation::forCallerIsolationInheriting();
4771+
}
4772+
}
4773+
47564774
// If the declaration is explicitly marked 'nonisolated', report it as
47574775
// independent.
47584776
if (nonisolatedAttr) {
@@ -4857,17 +4875,6 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true,
48574875
.withPreconcurrency(decl->preconcurrency() || isUnsafe);
48584876
}
48594877

4860-
// If the declaration is explicitly marked with 'execution', return the
4861-
// appropriate isolation.
4862-
if (concurrentExecutionAttr) {
4863-
switch (concurrentExecutionAttr->getBehavior()) {
4864-
case ExecutionKind::Concurrent:
4865-
return ActorIsolation::forNonisolated(false /*is unsafe*/);
4866-
case ExecutionKind::Caller:
4867-
return ActorIsolation::forCallerIsolationInheriting();
4868-
}
4869-
}
4870-
48714878
llvm_unreachable("Forgot about an attribute?");
48724879
}
48734880

@@ -5534,6 +5541,9 @@ static void addAttributesForActorIsolation(ValueDecl *value,
55345541
ASTContext &ctx = value->getASTContext();
55355542
switch (isolation) {
55365543
case ActorIsolation::CallerIsolationInheriting:
5544+
value->getAttrs().add(new (ctx) ExecutionAttr(ExecutionKind::Caller,
5545+
/*implicit=*/true));
5546+
break;
55375547
case ActorIsolation::Nonisolated:
55385548
case ActorIsolation::NonisolatedUnsafe: {
55395549
value->getAttrs().add(new (ctx) NonisolatedAttr(
@@ -5568,6 +5578,83 @@ static void addAttributesForActorIsolation(ValueDecl *value,
55685578
}
55695579
}
55705580

5581+
/// Determine the default isolation and isolation source for this declaration,
5582+
/// which may still be overridden by other inference rules.
5583+
static std::tuple<InferredActorIsolation, ValueDecl *,
5584+
std::optional<ActorIsolation>>
5585+
computeDefaultInferredActorIsolation(ValueDecl *value) {
5586+
auto &ctx = value->getASTContext();
5587+
5588+
// If we are supposed to infer main actor isolation by default for entities
5589+
// within our module, make our default isolation main actor.
5590+
if (ctx.LangOpts.hasFeature(Feature::UnspecifiedMeansMainActorIsolated) &&
5591+
value->getModuleContext() == ctx.MainModule) {
5592+
5593+
// Default global actor isolation does not apply to any declarations
5594+
// within actors and distributed actors.
5595+
bool inActorContext = false;
5596+
auto *dc = value->getInnermostDeclContext();
5597+
while (dc && !inActorContext) {
5598+
if (auto *nominal = dc->getSelfNominalTypeDecl()) {
5599+
inActorContext = nominal->isAnyActor();
5600+
}
5601+
dc = dc->getParent();
5602+
}
5603+
5604+
if (!inActorContext) {
5605+
// FIXME: deinit should be implicitly MainActor too.
5606+
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
5607+
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
5608+
isa<ConstructorDecl>(value)) {
5609+
return {{ActorIsolation::forMainActor(ctx), {}}, nullptr, {}};
5610+
}
5611+
}
5612+
}
5613+
5614+
// If we have an async function... by default we inherit isolation.
5615+
if (ctx.LangOpts.hasFeature(
5616+
Feature::NonIsolatedAsyncInheritsIsolationFromContext)) {
5617+
if (auto *func = dyn_cast<AbstractFunctionDecl>(value);
5618+
func && func->hasAsync() &&
5619+
func->getModuleContext() == ctx.MainModule) {
5620+
return {
5621+
{ActorIsolation::forCallerIsolationInheriting(), {}}, nullptr, {}};
5622+
}
5623+
}
5624+
5625+
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
5626+
// A @Sendable function is assumed to be actor-independent.
5627+
if (func->isSendable()) {
5628+
return {
5629+
{ActorIsolation::forNonisolated(/*unsafe=*/false), {}}, nullptr, {}};
5630+
}
5631+
}
5632+
5633+
// When no other isolation applies, an actor's non-async init is independent
5634+
if (auto nominal = value->getDeclContext()->getSelfNominalTypeDecl())
5635+
if (nominal->isAnyActor())
5636+
if (auto ctor = dyn_cast<ConstructorDecl>(value))
5637+
if (!ctor->hasAsync())
5638+
return {{ActorIsolation::forNonisolated(/*unsafe=*/false), {}},
5639+
nullptr,
5640+
{}};
5641+
5642+
// Look for and remember the overridden declaration's isolation.
5643+
if (auto *overriddenValue = value->getOverriddenDeclOrSuperDeinit()) {
5644+
// Use the overridden decl's isolation as the default isolation for this
5645+
// decl.
5646+
auto isolation = getOverriddenIsolationFor(value);
5647+
return {{isolation,
5648+
IsolationSource(overriddenValue, IsolationSource::Override)},
5649+
overriddenValue,
5650+
isolation}; // use the overridden decl's iso as the default
5651+
// isolation for this decl.
5652+
}
5653+
5654+
// We did not find anything special, return unspecified.
5655+
return {{ActorIsolation::forUnspecified(), {}}, nullptr, {}};
5656+
}
5657+
55715658
InferredActorIsolation ActorIsolationRequest::evaluate(
55725659
Evaluator &evaluator, ValueDecl *value) const {
55735660
// If this declaration has actor-isolated "self", it's isolated to that
@@ -5611,7 +5698,20 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
56115698
value->getAttrs().add(preconcurrency);
56125699
}
56135700

5614-
if (FuncDecl *fd = dyn_cast<FuncDecl>(value)) {
5701+
// Check if we inferred CallerIsolationInheriting from our isolation attr, but
5702+
// did not have an ExecutionKind::Caller attached to it.
5703+
//
5704+
// DISCUSSION: This occurs when we have a value decl that is explicitly marked
5705+
// as nonisolated but since NonIsolatedAsyncInheritsIsolationFromContext is
5706+
// enabled, we return CallerIsolationInheriting.
5707+
if (isolationFromAttr && isolationFromAttr->getKind() ==
5708+
ActorIsolation::CallerIsolationInheriting &&
5709+
!value->getAttrs().hasAttribute<ExecutionAttr>()) {
5710+
value->getAttrs().add(new (ctx) ExecutionAttr(ExecutionKind::Caller,
5711+
/*implicit=*/true));
5712+
}
5713+
5714+
if (auto *fd = dyn_cast<FuncDecl>(value)) {
56155715
// Main.main() and Main.$main are implicitly MainActor-protected.
56165716
// Any other isolation is an error.
56175717
std::optional<ActorIsolation> mainIsolation =
@@ -5644,74 +5744,11 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
56445744
IsolationSource(/*source*/ nullptr, IsolationSource::Explicit)};
56455745
}
56465746

5647-
// Determine the default isolation for this declaration, which may still be
5648-
// overridden by other inference rules.
5649-
ActorIsolation defaultIsolation = ActorIsolation::forUnspecified();
5650-
IsolationSource defaultIsolationSource;
5651-
5652-
// If we are supposed to infer main actor isolation by default for entities
5653-
// within our module, make our default isolation main actor.
5654-
if (ctx.LangOpts.hasFeature(Feature::UnspecifiedMeansMainActorIsolated) &&
5655-
value->getModuleContext() == ctx.MainModule) {
5656-
5657-
// Default global actor isolation does not apply to any declarations
5658-
// within actors and distributed actors.
5659-
bool inActorContext = false;
5660-
auto *dc = value->getInnermostDeclContext();
5661-
while (dc && !inActorContext) {
5662-
if (auto *nominal = dc->getSelfNominalTypeDecl()) {
5663-
inActorContext = nominal->isAnyActor();
5664-
}
5665-
dc = dc->getParent();
5666-
}
5667-
5668-
if (!inActorContext) {
5669-
// FIXME: deinit should be implicitly MainActor too.
5670-
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
5671-
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
5672-
isa<ConstructorDecl>(value)) {
5673-
defaultIsolation = ActorIsolation::forMainActor(ctx);
5674-
}
5675-
}
5676-
}
5677-
5678-
// If we have an async function... by default we inherit isolation.
5679-
if (ctx.LangOpts.hasFeature(
5680-
Feature::NonIsolatedAsyncInheritsIsolationFromContext)) {
5681-
if (auto *func = dyn_cast<AbstractFunctionDecl>(value);
5682-
func && func->hasAsync() &&
5683-
func->getModuleContext() == ctx.MainModule) {
5684-
defaultIsolation = ActorIsolation::forCallerIsolationInheriting();
5685-
}
5686-
}
5687-
5688-
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
5689-
// A @Sendable function is assumed to be actor-independent.
5690-
if (func->isSendable()) {
5691-
defaultIsolation = ActorIsolation::forNonisolated(/*unsafe=*/false);
5692-
}
5693-
}
5694-
5695-
// When no other isolation applies, an actor's non-async init is independent
5696-
if (auto nominal = value->getDeclContext()->getSelfNominalTypeDecl())
5697-
if (nominal->isAnyActor())
5698-
if (auto ctor = dyn_cast<ConstructorDecl>(value))
5699-
if (!ctor->hasAsync())
5700-
defaultIsolation = ActorIsolation::forNonisolated(/*unsafe=*/false);
5701-
5702-
// Look for and remember the overridden declaration's isolation.
5703-
std::optional<ActorIsolation> overriddenIso;
5704-
ValueDecl *overriddenValue = value->getOverriddenDeclOrSuperDeinit();
5705-
if (overriddenValue) {
5706-
// use the overridden decl's iso as the default isolation for this decl.
5707-
defaultIsolation = getOverriddenIsolationFor(value);
5708-
defaultIsolationSource =
5709-
IsolationSource(overriddenValue, IsolationSource::Override);
5710-
overriddenIso = defaultIsolation;
5711-
}
5712-
5713-
// NOTE: After this point, the default has been set. Only touch the default
5714-
// isolation above this point since code below assumes it is now constant.
5747+
InferredActorIsolation defaultIsolation;
5748+
ValueDecl *overriddenValue;
5749+
std::optional<ActorIsolation> overridenIsolation;
5750+
std::tie(defaultIsolation, overriddenValue, overridenIsolation) =
5751+
computeDefaultInferredActorIsolation(value);
57155752

57165753
// Function used when returning an inferred isolation.
57175754
auto inferredIsolation = [&](ActorIsolation inferred,
@@ -5722,16 +5759,17 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
57225759
// if the inferred isolation is not valid, then carry-over the overridden
57235760
// declaration's isolation as this decl's inferred isolation.
57245761
switch (validOverrideIsolation(value, inferred, overriddenValue,
5725-
*overriddenIso)) {
5762+
*overridenIsolation)) {
57265763
case OverrideIsolationResult::Allowed:
57275764
case OverrideIsolationResult::Sendable:
57285765
break;
57295766

57305767
case OverrideIsolationResult::Disallowed:
5731-
if (overriddenValue->hasClangNode() && overriddenIso->isUnspecified()) {
5732-
inferred = overriddenIso->withPreconcurrency(true);
5768+
if (overriddenValue->hasClangNode() &&
5769+
overridenIsolation->isUnspecified()) {
5770+
inferred = overridenIsolation->withPreconcurrency(true);
57335771
} else {
5734-
inferred = *overriddenIso;
5772+
inferred = *overridenIsolation;
57355773
}
57365774
break;
57375775
}
@@ -5758,6 +5796,7 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
57585796
addAttributesForActorIsolation(value, inferred);
57595797
break;
57605798
case ActorIsolation::CallerIsolationInheriting:
5799+
addAttributesForActorIsolation(value, inferred);
57615800
break;
57625801
case ActorIsolation::Erased:
57635802
llvm_unreachable("cannot infer erased isolation");
@@ -5988,8 +6027,18 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
59886027
}
59896028
}
59906029

5991-
// Default isolation for this member.
5992-
return {defaultIsolation, defaultIsolationSource};
6030+
// We did not invoke any earlier rules... so just return the default
6031+
// isolation.
6032+
if (defaultIsolation.isolation.getKind() ==
6033+
ActorIsolation::CallerIsolationInheriting) {
6034+
// If we have caller isolation inheriting, attach the attribute for it so
6035+
// that we preserve that we chose CallerIsolationInheriting through
6036+
// serialization. We do this since we need to support compiling where
6037+
// nonisolated is the default and where caller isolation inheriting is the
6038+
// default.
6039+
addAttributesForActorIsolation(value, defaultIsolation.isolation);
6040+
}
6041+
return defaultIsolation;
59936042
}
59946043

59956044
bool HasIsolatedSelfRequest::evaluate(

test/SILGen/execution_attr.swift

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
// RUN: %target-swift-emit-silgen %s -enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext | %FileCheck %s
3+
4+
// REQUIRES: concurrency
5+
// REQUIRES: asserts
6+
// REQUIRES: swift_feature_NonIsolatedAsyncInheritsIsolationFromContext
7+
8+
// Validate that both with and without the experimental flag we properly codegen
9+
// execution(caller) and execution(concurrent).
10+
11+
// CHECK-LABEL: // executionCaller()
12+
// CHECK-NEXT: // Isolation: caller_isolation_inheriting
13+
// CHECK-NEXT: sil hidden [ossa] @$s14execution_attr0A6CalleryyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> () {
14+
@execution(caller)
15+
func executionCaller() async {}
16+
17+
// CHECK-LABEL: // executionConcurrent()
18+
// CHECK: // Isolation: nonisolated
19+
// CHECK: sil hidden [ossa] @$s14execution_attr0A10ConcurrentyyYaF : $@convention(thin) @async () -> () {
20+
@execution(concurrent)
21+
func executionConcurrent() async {}

0 commit comments

Comments
 (0)