Skip to content

Commit 515221c

Browse files
authored
Merge pull request #79375 from xedin/isolation-caller-in-type-context
[AST/Sema] Add new function type isolation - `caller` to cover `@execution(caller)` attribute
2 parents 654f853 + 7c3fb63 commit 515221c

File tree

13 files changed

+210
-31
lines changed

13 files changed

+210
-31
lines changed

include/swift/AST/ExtInfo.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ class FunctionTypeIsolation {
6363

6464
/// The function's isolation is statically erased with @isolated(any).
6565
Erased,
66+
67+
/// Inherits isolation from the caller. This is only applicable
68+
/// to asynchronous function types.
69+
///
70+
/// NOTE: The difference in between NonIsolatedCaller and
71+
/// NonIsolated is that NonIsolatedCaller is a strictly
72+
/// weaker form of nonisolation. While both in their bodies cannot
73+
/// access isolated state directly, NonIsolatedCaller functions
74+
/// /are/ allowed to access state isolated to their caller via
75+
/// function arguments since we know that the callee will stay
76+
/// in the caller's isolation domain. In contrast, NonIsolated
77+
/// is strongly nonisolated and is not allowed to access /any/
78+
/// isolated state (even via function parameters) since it is
79+
/// considered safe to run on /any/ actor.
80+
NonIsolatedCaller,
6681
};
6782

6883
static constexpr size_t NumBits = 3; // future-proof this slightly
@@ -87,6 +102,9 @@ class FunctionTypeIsolation {
87102
static FunctionTypeIsolation forErased() {
88103
return { Kind::Erased };
89104
}
105+
static FunctionTypeIsolation forNonIsolatedCaller() {
106+
return { Kind::NonIsolatedCaller };
107+
}
90108

91109
Kind getKind() const { return value.getInt(); }
92110
bool isNonIsolated() const {

lib/AST/ASTMangler.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3285,6 +3285,11 @@ void ASTMangler::appendFunctionSignature(AnyFunctionType *fn,
32853285
if (AllowIsolatedAny)
32863286
appendOperator("YA");
32873287
break;
3288+
3289+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
3290+
// TODO: We need a special mangling for this to
3291+
// make it distinct from the `@execution(concurrent)`.
3292+
break;
32883293
}
32893294

32903295
if (isRecursedInto && fn->hasSendingResult()) {

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6421,6 +6421,10 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
64216421
if (!Options.SuppressIsolatedAny)
64226422
Printer << "@isolated(any) ";
64236423
break;
6424+
6425+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
6426+
Printer << "@execution(caller) ";
6427+
break;
64246428
}
64256429

64266430
if (!Options.excludeAttrKind(TypeAttrKind::Sendable) && info.isSendable()) {

lib/SILGen/SILGenConcurrency.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,7 @@ SILGenFunction::emitFunctionTypeIsolation(SILLocation loc,
563563

564564
// Emit nonisolated by simply emitting Optional.none in the result type.
565565
case FunctionTypeIsolation::Kind::NonIsolated:
566+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
566567
return emitNonIsolatedIsolation(loc);
567568

568569
// Emit global actor isolation by loading .shared from the global actor,

lib/SILGen/SILGenPoly.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5439,6 +5439,10 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
54395439
case FunctionTypeIsolation::Kind::NonIsolated:
54405440
break;
54415441

5442+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
5443+
hopToIsolatedParameter = true;
5444+
break;
5445+
54425446
// For a function with parameter isolation, we'll have to dig the
54435447
// argument out after translation but before making the call.
54445448
case FunctionTypeIsolation::Kind::Parameter:

lib/Sema/CSSimplify.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2964,6 +2964,10 @@ ConstraintSystem::matchFunctionIsolations(FunctionType *func1,
29642964
case FunctionTypeIsolation::Kind::NonIsolated:
29652965
return true;
29662966

2967+
// A thunk is going to pass `nil` to the isolated parameter.
2968+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
2969+
return matchIfConversion();
2970+
29672971
// Erasing global-actor isolation to non-isolation can admit data
29682972
// races; such violations are diagnosed by the actor isolation checker.
29692973
// We deliberately do not allow actor isolation violations to influence
@@ -2984,6 +2988,35 @@ ConstraintSystem::matchFunctionIsolations(FunctionType *func1,
29842988
}
29852989
llvm_unreachable("bad kind");
29862990

2991+
// Converting to a caller isolated async function type.
2992+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
2993+
switch (isolation1.getKind()) {
2994+
// Exact match.
2995+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
2996+
return true;
2997+
2998+
// Global actor: Thunk will hop to the global actor
2999+
// and would ignore passed in isolation.
3000+
// Erased: Just like global actor but would hop to
3001+
// the isolation stored in the @isolated(any) function.
3002+
case FunctionTypeIsolation::Kind::GlobalActor:
3003+
case FunctionTypeIsolation::Kind::Erased:
3004+
return matchIfConversion();
3005+
3006+
// In this case the isolation is dependent on a
3007+
// specific actor passed in as the isolation parameter
3008+
// and the thunk won't have it.
3009+
case FunctionTypeIsolation::Kind::Parameter:
3010+
return false;
3011+
3012+
// For asynchronous: Thunk would hop the appropriate actor.
3013+
// For synchronous: Thunk would call the function without
3014+
// a hop.
3015+
case FunctionTypeIsolation::Kind::NonIsolated:
3016+
return matchIfConversion();
3017+
}
3018+
llvm_unreachable("bad kind");
3019+
29873020
// Converting to a global-actor-isolated type.
29883021
case FunctionTypeIsolation::Kind::GlobalActor:
29893022
switch (isolation1.getKind()) {
@@ -3004,6 +3037,11 @@ ConstraintSystem::matchFunctionIsolations(FunctionType *func1,
30043037
case FunctionTypeIsolation::Kind::NonIsolated:
30053038
return matchIfConversion();
30063039

3040+
// A thunk is going to pass in an instance of a global actor
3041+
// to the isolated parameter.
3042+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
3043+
return matchIfConversion();
3044+
30073045
// Parameter isolation cannot be altered in the same way.
30083046
case FunctionTypeIsolation::Kind::Parameter:
30093047
return false;
@@ -3030,6 +3068,10 @@ ConstraintSystem::matchFunctionIsolations(FunctionType *func1,
30303068
case FunctionTypeIsolation::Kind::GlobalActor:
30313069
return matchIfConversion();
30323070

3071+
// A thunk is going to forward the isolation.
3072+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
3073+
return matchIfConversion();
3074+
30333075
// Don't allow dynamically-isolated function types to convert to
30343076
// any specific isolation for the same policy reasons that we don't
30353077
// want to allow global-actors to change.
@@ -3050,6 +3092,11 @@ ConstraintSystem::matchFunctionIsolations(FunctionType *func1,
30503092
case FunctionTypeIsolation::Kind::GlobalActor:
30513093
return matchIfConversion(/*erasure*/ true);
30523094

3095+
// It's not possible to form a thunk for this case because
3096+
// we don't know what to pass to the isolated parameter.
3097+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
3098+
return false;
3099+
30533100
// Parameter isolation is value-dependent and can't be erased in the
30543101
// abstract, though. We need to be able to recover the isolation from
30553102
// a value.

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7081,15 +7081,19 @@ AnyFunctionType *swift::adjustFunctionTypeForConcurrency(
70817081

70827082
fnType = applyUnsafeConcurrencyToFunctionType(
70837083
fnType, decl, strictChecking, numApplies, isMainDispatchQueue);
7084-
Type globalActorType;
7084+
std::optional<FunctionTypeIsolation> funcIsolation;
70857085
if (decl) {
70867086
switch (auto isolation = getActorIsolation(decl)) {
70877087
case ActorIsolation::ActorInstance:
70887088
// The function type may or may not have parameter isolation.
70897089
return fnType;
70907090

7091-
case ActorIsolation::Nonisolated:
70927091
case ActorIsolation::CallerIsolationInheriting:
7092+
assert(fnType->getIsolation().isNonIsolated());
7093+
funcIsolation = FunctionTypeIsolation::forNonIsolatedCaller();
7094+
break;
7095+
7096+
case ActorIsolation::Nonisolated:
70937097
case ActorIsolation::NonisolatedUnsafe:
70947098
case ActorIsolation::Unspecified:
70957099
assert(fnType->getIsolation().isNonIsolated());
@@ -7104,20 +7108,22 @@ AnyFunctionType *swift::adjustFunctionTypeForConcurrency(
71047108
if (!strictChecking && isolation.preconcurrency())
71057109
return fnType;
71067110

7107-
globalActorType = openType(isolation.getGlobalActor());
7111+
Type globalActorType = openType(isolation.getGlobalActor());
7112+
funcIsolation = FunctionTypeIsolation::forGlobalActor(globalActorType);
71087113
break;
71097114
}
71107115
}
71117116

7112-
auto isolation = FunctionTypeIsolation::forGlobalActor(globalActorType);
7117+
ASSERT(funcIsolation.has_value());
71137118

71147119
// If there's no implicit "self" declaration, apply the isolation to
71157120
// the outermost function type.
71167121
bool hasImplicitSelfDecl = decl && (isa<EnumElementDecl>(decl) ||
71177122
(isa<AbstractFunctionDecl>(decl) &&
71187123
cast<AbstractFunctionDecl>(decl)->hasImplicitSelfDecl()));
71197124
if (!hasImplicitSelfDecl) {
7120-
return fnType->withExtInfo(fnType->getExtInfo().withIsolation(isolation));
7125+
return fnType->withExtInfo(
7126+
fnType->getExtInfo().withIsolation(*funcIsolation));
71217127
}
71227128

71237129
// Dig out the inner function type.
@@ -7127,7 +7133,7 @@ AnyFunctionType *swift::adjustFunctionTypeForConcurrency(
71277133

71287134
// Update the inner function type with the isolation.
71297135
innerFnType = innerFnType->withExtInfo(
7130-
innerFnType->getExtInfo().withIsolation(isolation));
7136+
innerFnType->getExtInfo().withIsolation(*funcIsolation));
71317137

71327138
// Rebuild the outer function type around it.
71337139
if (auto genericFnType = dyn_cast<GenericFunctionType>(fnType)) {

lib/Sema/TypeCheckType.cpp

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4179,40 +4179,46 @@ NeverNullType TypeResolver::resolveASTFunctionType(
41794179
diag::attr_execution_type_attr_only_on_async);
41804180
}
41814181

4182-
switch (isolation.getKind()) {
4183-
case FunctionTypeIsolation::Kind::NonIsolated:
4184-
break;
4182+
if (executionAttr->getBehavior() == ExecutionKind::Concurrent) {
4183+
switch (isolation.getKind()) {
4184+
case FunctionTypeIsolation::Kind::NonIsolated:
4185+
break;
41854186

4186-
case FunctionTypeIsolation::Kind::GlobalActor:
4187-
diagnoseInvalid(
4188-
repr, executionAttr->getAtLoc(),
4189-
diag::
4190-
attr_execution_concurrent_type_attr_incompatible_with_global_isolation,
4191-
isolation.getGlobalActorType());
4192-
break;
4187+
case FunctionTypeIsolation::Kind::GlobalActor:
4188+
diagnoseInvalid(
4189+
repr, executionAttr->getAtLoc(),
4190+
diag::
4191+
attr_execution_concurrent_type_attr_incompatible_with_global_isolation,
4192+
isolation.getGlobalActorType());
4193+
break;
41934194

4194-
case FunctionTypeIsolation::Kind::Parameter:
4195-
diagnoseInvalid(
4196-
repr, executionAttr->getAtLoc(),
4197-
diag::
4198-
attr_execution_concurrent_type_attr_incompatible_with_isolated_param);
4199-
break;
4195+
case FunctionTypeIsolation::Kind::Parameter:
4196+
diagnoseInvalid(
4197+
repr, executionAttr->getAtLoc(),
4198+
diag::
4199+
attr_execution_concurrent_type_attr_incompatible_with_isolated_param);
4200+
break;
42004201

4201-
case FunctionTypeIsolation::Kind::Erased:
4202-
diagnoseInvalid(
4203-
repr, executionAttr->getAtLoc(),
4204-
diag::
4205-
attr_execution_concurrent_type_attr_incompatible_with_isolated_any);
4206-
break;
4202+
case FunctionTypeIsolation::Kind::Erased:
4203+
diagnoseInvalid(
4204+
repr, executionAttr->getAtLoc(),
4205+
diag::
4206+
attr_execution_concurrent_type_attr_incompatible_with_isolated_any);
4207+
break;
4208+
4209+
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
4210+
llvm_unreachable("cannot happen because multiple @execution attributes "
4211+
"aren't allowed.");
4212+
}
42074213
}
42084214

42094215
if (!repr->isInvalid()) {
42104216
switch (executionAttr->getBehavior()) {
42114217
case ExecutionKind::Concurrent:
4212-
// TODO: We need to introduce a new isolation kind to support this.
4218+
isolation = FunctionTypeIsolation::forNonIsolated();
42134219
break;
42144220
case ExecutionKind::Caller:
4215-
isolation = FunctionTypeIsolation::forNonIsolated();
4221+
isolation = FunctionTypeIsolation::forNonIsolatedCaller();
42164222
break;
42174223
}
42184224
}

lib/Serialization/Deserialization.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7133,6 +7133,8 @@ detail::function_deserializer::deserialize(ModuleFile &MF,
71337133
auto isolation = swift::FunctionTypeIsolation::forNonIsolated();
71347134
if (rawIsolation == unsigned(FunctionTypeIsolation::NonIsolated)) {
71357135
// do nothing
7136+
} else if (rawIsolation == unsigned(FunctionTypeIsolation::NonIsolatedCaller)) {
7137+
isolation = swift::FunctionTypeIsolation::forNonIsolatedCaller();
71367138
} else if (rawIsolation == unsigned(FunctionTypeIsolation::Parameter)) {
71377139
isolation = swift::FunctionTypeIsolation::forParameter();
71387140
} else if (rawIsolation == unsigned(FunctionTypeIsolation::Erased)) {

lib/Serialization/ModuleFormat.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 921; // remove alloc_vector
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 922; // function type isolation - caller
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -707,6 +707,7 @@ enum class FunctionTypeIsolation : uint8_t {
707707
Parameter,
708708
Erased,
709709
GlobalActorOffset, // Add this to the global actor type ID
710+
NonIsolatedCaller,
710711
};
711712
using FunctionTypeIsolationField = TypeIDField;
712713

lib/Serialization/Serialization.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5781,6 +5781,8 @@ class Serializer::TypeSerializer : public TypeVisitor<TypeSerializer> {
57815781
switch (isolation.getKind()) {
57825782
case swift::FunctionTypeIsolation::Kind::NonIsolated:
57835783
return unsigned(FunctionTypeIsolation::NonIsolated);
5784+
case swift::FunctionTypeIsolation::Kind::NonIsolatedCaller:
5785+
return unsigned(FunctionTypeIsolation::NonIsolatedCaller);
57845786
case swift::FunctionTypeIsolation::Kind::Parameter:
57855787
return unsigned(FunctionTypeIsolation::Parameter);
57865788
case swift::FunctionTypeIsolation::Kind::Erased:
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple
2+
3+
// REQUIRES: concurrency
4+
5+
@execution(concurrent)
6+
func concurrentTest() async {
7+
}
8+
9+
@execution(caller)
10+
func callerTest() async {
11+
}
12+
13+
@MainActor
14+
func actorIsolated() async {}
15+
16+
let _: @execution(caller) () async -> Void = concurrentTest // Ok
17+
let _: @execution(concurrent) () async -> Void = callerTest // Ok
18+
19+
let _: @MainActor () async -> Void = concurrentTest // Ok
20+
let _: @MainActor () async -> Void = callerTest // Ok
21+
22+
let _: @isolated(any) () async -> Void = concurrentTest // Ok
23+
let _: @isolated(any) () async -> Void = callerTest
24+
// expected-error@-1 {{cannot convert value of type '@execution(caller) () async -> ()' to specified type '@isolated(any) () async -> Void'}}
25+
26+
let _: @execution(caller) () async -> Void = actorIsolated // Ok
27+
let _: @execution(concurrent) () async -> Void = actorIsolated // Ok
28+
29+
func testIsolationErasure(fn: @escaping @isolated(any) () async -> Void) {
30+
let _: @execution(concurrent) () async -> Void = fn // Ok
31+
let _: @execution(caller) () async -> Void = fn // Ok
32+
}
33+
34+
func testUpcast(arr: [@execution(caller) () async -> Void]) {
35+
let _: [() async -> Void] = arr // Ok - collection upcast
36+
let _: [String: () async -> Void] = ["": arr]
37+
// expected-error@-1 {{cannot convert value of type '[@execution(caller) () async -> Void]' to expected dictionary value type '() async -> Void'}}
38+
}
39+
40+
// Isolated parameter
41+
func testParameterIsolation(fn: @escaping (isolated (any Actor)?) async -> Void) {
42+
let _: @execution(caller) () async -> Void = fn
43+
// expected-error@-1 {{cannot convert value of type '(isolated (any Actor)?) async -> Void' to specified type '@execution(caller) () async -> Void'}}
44+
let _: @execution(concurrent) () async -> Void = fn
45+
// expected-error@-1 {{cannot convert value of type '(isolated (any Actor)?) async -> Void' to specified type '() async -> Void'}}
46+
}
47+
48+
// Non-conversion situations
49+
do {
50+
struct S<T> {
51+
}
52+
func test<T>(_: S<T>, _: T.Type) {}
53+
54+
test(S<() async -> Void>(), type(of: callerTest))
55+
// expected-error@-1 {{cannot convert value of type '(@execution(caller) () async -> ()).Type' to expected argument type '(() async -> Void).Type'}}
56+
57+
test(S<@execution(caller) () async -> Void>(), type(of: concurrentTest))
58+
// expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(@execution(caller) () async -> Void).Type'}}
59+
60+
test(S<@MainActor () async -> Void>(), type(of: callerTest))
61+
// expected-error@-1 {{cannot convert value of type '(@execution(caller) () async -> ()).Type' to expected argument type '(@MainActor () async -> Void).Type'}}
62+
63+
test(S<@MainActor () async -> Void>(), type(of: concurrentTest))
64+
// expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(@MainActor () async -> Void).Type'}}
65+
66+
test(S<(isolated (any Actor)?) async -> Void>(), type(of: callerTest))
67+
// expected-error@-1 {{cannot convert value of type '(@execution(caller) () async -> ()).Type' to expected argument type '((isolated (any Actor)?) async -> Void).Type'}}
68+
69+
test(S<(isolated (any Actor)?) async -> Void>(), type(of: concurrentTest))
70+
// expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '((isolated (any Actor)?) async -> Void).Type'}}
71+
72+
test(S<@isolated(any) () async -> Void>(), type(of: concurrentTest))
73+
// expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(@isolated(any) () async -> Void).Type'}}
74+
test(S<@isolated(any) () async -> Void>(), type(of: callerTest))
75+
// expected-error@-1 {{cannot convert value of type '(@execution(caller) () async -> ()).Type' to expected argument type '(@isolated(any) () async -> Void).Type'}}
76+
}
77+

0 commit comments

Comments
 (0)