Skip to content

Commit cdb7de1

Browse files
committed
[Concurrency] References to global-actor functions have global-actor-qualified type.
When referencing a function that is on a global actor, e.g., @mainactor func doSomething() -> Int the result of that reference is a global-actor-qualified function type, e.g., @mainactor () -> Int Part of rdar://76030136.
1 parent f53f80f commit cdb7de1

File tree

6 files changed

+105
-18
lines changed

6 files changed

+105
-18
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,16 @@ unwrapPropertyWrapperParameterTypes(ConstraintSystem &cs, AbstractFunctionDecl *
12331233
functionType->getExtInfo());
12341234
}
12351235

1236+
/// Determine whether the given locator is for a witness or requirement.
1237+
static bool isRequirementOrWitness(const ConstraintLocatorBuilder &locator) {
1238+
if (auto last = locator.last()) {
1239+
return last->getKind() == ConstraintLocator::ProtocolRequirement ||
1240+
last->getKind() == ConstraintLocator::Witness;
1241+
}
1242+
1243+
return false;
1244+
}
1245+
12361246
std::pair<Type, Type>
12371247
ConstraintSystem::getTypeOfReference(ValueDecl *value,
12381248
FunctionRefKind functionRefKind,
@@ -1245,9 +1255,12 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
12451255

12461256
OpenedTypeMap replacements;
12471257

1248-
auto openedType =
1249-
openFunctionType(func->getInterfaceType()->castTo<AnyFunctionType>(),
1250-
locator, replacements, func->getDeclContext());
1258+
AnyFunctionType *funcType = func->getInterfaceType()
1259+
->castTo<AnyFunctionType>();
1260+
if (!isRequirementOrWitness(locator))
1261+
funcType = applyGlobalActorType(funcType, func, useDC);
1262+
auto openedType = openFunctionType(
1263+
funcType, locator, replacements, func->getDeclContext());
12511264

12521265
// If we opened up any type variables, record the replacements.
12531266
recordOpenedTypes(locator, replacements);
@@ -1274,10 +1287,12 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
12741287
auto numLabelsToRemove = getNumRemovedArgumentLabels(
12751288
funcDecl, /*isCurriedInstanceReference=*/false, functionRefKind);
12761289

1290+
if (!isRequirementOrWitness(locator))
1291+
funcType = applyGlobalActorType(funcType, funcDecl, useDC);
1292+
12771293
auto openedType = openFunctionType(funcType, locator, replacements,
12781294
funcDecl->getDeclContext())
12791295
->removeArgumentLabels(numLabelsToRemove);
1280-
12811296
openedType = unwrapPropertyWrapperParameterTypes(*this, funcDecl, functionRefKind,
12821297
openedType->getAs<FunctionType>(),
12831298
locator);
@@ -1514,16 +1529,6 @@ static void addSelfConstraint(ConstraintSystem &cs, Type objectTy, Type selfTy,
15141529
cs.getConstraintLocator(locator));
15151530
}
15161531

1517-
/// Determine whether the given locator is for a witness or requirement.
1518-
static bool isRequirementOrWitness(const ConstraintLocatorBuilder &locator) {
1519-
if (auto last = locator.last()) {
1520-
return last->getKind() == ConstraintLocator::ProtocolRequirement ||
1521-
last->getKind() == ConstraintLocator::Witness;
1522-
}
1523-
1524-
return false;
1525-
}
1526-
15271532
Type constraints::getDynamicSelfReplacementType(
15281533
Type baseObjTy, const ValueDecl *member, ConstraintLocator *memberLocator) {
15291534
// Constructions must always have their dynamic 'Self' result type replaced
@@ -1623,6 +1628,9 @@ ConstraintSystem::getTypeOfMemberReference(
16231628
isa<EnumElementDecl>(value)) {
16241629
// This is the easy case.
16251630
funcType = value->getInterfaceType()->castTo<AnyFunctionType>();
1631+
1632+
if (!isRequirementOrWitness(locator))
1633+
funcType = applyGlobalActorType(funcType, value, useDC);
16261634
} else {
16271635
// For a property, build a type (Self) -> PropType.
16281636
// For a subscript, build a type (Self) -> (Indices...) -> ElementType.

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3237,3 +3237,55 @@ NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
32373237

32383238
return conformance;
32393239
}
3240+
3241+
AnyFunctionType *swift::applyGlobalActorType(
3242+
AnyFunctionType *fnType, ValueDecl *funcOrEnum, DeclContext *dc) {
3243+
Type globalActorType;
3244+
switch (auto isolation = getActorIsolation(funcOrEnum)) {
3245+
case ActorIsolation::ActorInstance:
3246+
case ActorIsolation::Independent:
3247+
case ActorIsolation::Unspecified:
3248+
return fnType;
3249+
3250+
case ActorIsolation::GlobalActorUnsafe:
3251+
// Only treat as global-actor-qualified within code that has adopted
3252+
// Swift Concurrency features.
3253+
if (!contextUsesConcurrencyFeatures(dc))
3254+
return fnType;
3255+
3256+
LLVM_FALLTHROUGH;
3257+
3258+
case ActorIsolation::GlobalActor:
3259+
globalActorType = isolation.getGlobalActor();
3260+
break;
3261+
}
3262+
3263+
// If there's no implicit "self" declaration, apply the global actor to
3264+
// the outermost function type.
3265+
bool hasImplicitSelfDecl = isa<EnumElementDecl>(funcOrEnum) ||
3266+
(isa<AbstractFunctionDecl>(funcOrEnum) &&
3267+
cast<AbstractFunctionDecl>(funcOrEnum)->hasImplicitSelfDecl());
3268+
if (!hasImplicitSelfDecl) {
3269+
return fnType->withExtInfo(
3270+
fnType->getExtInfo().withGlobalActor(globalActorType));
3271+
}
3272+
3273+
// Dig out the inner function type.
3274+
auto innerFnType = fnType->getResult()->getAs<AnyFunctionType>();
3275+
if (!innerFnType)
3276+
return fnType;
3277+
3278+
// Update the inner function type with the global actor.
3279+
innerFnType = innerFnType->withExtInfo(
3280+
innerFnType->getExtInfo().withGlobalActor(globalActorType));
3281+
3282+
// Rebuild the outer function type around it.
3283+
if (auto genericFnType = dyn_cast<GenericFunctionType>(fnType)) {
3284+
return GenericFunctionType::get(
3285+
genericFnType->getGenericSignature(), fnType->getParams(),
3286+
Type(innerFnType), fnType->getExtInfo());
3287+
}
3288+
3289+
return FunctionType::get(
3290+
fnType->getParams(), Type(innerFnType), fnType->getExtInfo());
3291+
}

lib/Sema/TypeChecker.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,12 @@ void checkUnknownAttrRestrictions(
13231323
/// it to later stages.
13241324
void bindSwitchCasePatternVars(DeclContext *dc, CaseStmt *stmt);
13251325

1326+
/// If the given function has a global actor that should be reflected in
1327+
/// references to its function type from the given declaration context,
1328+
/// update the given function type to include the global actor.
1329+
AnyFunctionType *applyGlobalActorType(
1330+
AnyFunctionType *fnType, ValueDecl *funcOrEnum, DeclContext *dc);
1331+
13261332
} // end namespace swift
13271333

13281334
#endif

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ func blender(_ peeler : () -> Void) {
292292
// expected-warning@-1 3{{cannot pass argument of non-sendable type 'Any' across actors}}
293293

294294
blender((peelBanana)) // expected-error {{global function 'peelBanana()' isolated to global actor 'BananaActor' can not be referenced from different global actor 'OrangeActor'}}
295+
// expected-error@-1{{converting function value of type '@BananaActor () -> ()' to '() -> Void' loses global actor 'BananaActor'}}
295296
await wisk(peelBanana) // expected-error {{global function 'peelBanana()' isolated to global actor 'BananaActor' can not be referenced from different global actor 'OrangeActor'}}
296297
// expected-warning@-1{{cannot pass argument of non-sendable type 'Any' across actors}}
297298

@@ -556,4 +557,4 @@ func tryTheActorSubscripts(a : SubscriptA, t : SubscriptT, at : SubscriptAT) asy
556557
_ = at[0]
557558

558559
_ = try await at[0]
559-
}
560+
}

test/Concurrency/global_actor_function_types.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ func testConversions(f: @escaping @SomeGlobalActor (Int) -> Void, g: @escaping (
2323
let _: @OtherGlobalActor (Int) -> Void = f // expected-error{{cannot convert value of type 'SomeGlobalActor' to specified type 'OtherGlobalActor'}}
2424
}
2525

26-
@SomeGlobalActor func onSomeGlobalActor() -> Int { 5 }
27-
@SomeGlobalActor(unsafe) func onSomeGlobalActorUnsafe() -> Int { 5 }
26+
@SomeGlobalActor func onSomeGlobalActor() -> Int { 5 } // expected-note 2{{calls to global function 'onSomeGlobalActor()' from outside of its actor context are implicitly asynchronous}}
27+
@SomeGlobalActor(unsafe) func onSomeGlobalActorUnsafe() -> Int { 5 } // expected-note{{calls to global function 'onSomeGlobalActorUnsafe()' from outside of its actor context are implicitly asynchronous}}
2828

2929
@OtherGlobalActor func onOtherGlobalActor() -> Int { 5 } // expected-note{{calls to global function 'onOtherGlobalActor()' from outside of its actor context are implicitly asynchronous}}
3030
@OtherGlobalActor(unsafe) func onOtherGlobalActorUnsafe() -> Int { 5 } // expected-note 2{{calls to global function 'onOtherGlobalActorUnsafe()' from outside of its actor context are implicitly asynchronous}}
@@ -109,3 +109,20 @@ func f2(_ x: X<@SomeGlobalActor () -> Void>) {
109109
func g2(_ x: X<() -> Void>) {
110110
f2(x) // expected-error{{cannot convert value of type 'X<() -> Void>' to expected argument type 'X<@SomeGlobalActor () -> Void>'}}
111111
}
112+
113+
114+
func testTypesNonConcurrencyContext() { // expected-note{{add '@SomeGlobalActor' to make global function 'testTypesNonConcurrencyContext()' part of global actor 'SomeGlobalActor'}}
115+
let f1 = onSomeGlobalActor // expected-error{{isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
116+
let f2 = onSomeGlobalActorUnsafe
117+
118+
let _: () -> Int = f1 // expected-error{{converting function value of type '@SomeGlobalActor () -> Int' to '() -> Int' loses global actor 'SomeGlobalActor'}}
119+
let _: () -> Int = f2
120+
}
121+
122+
func testTypesConcurrencyContext() async {
123+
let f1 = onSomeGlobalActor // expected-error{{isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
124+
let f2 = onSomeGlobalActorUnsafe // expected-error{{isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
125+
126+
let _: () -> Int = f1 // expected-error{{converting function value of type '@SomeGlobalActor () -> Int' to '() -> Int' loses global actor 'SomeGlobalActor'}}
127+
let _: () -> Int = f2 // expected-error{{converting function value of type '@SomeGlobalActor () -> Int' to '() -> Int' loses global actor 'SomeGlobalActor'}}
128+
}

test/Sema/option-set-empty.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown
22

33
struct SomeOptions: OptionSet {
4+
// expected-error@-1{{circular reference}}
5+
// expected-note@-2 2{{through reference here}}
46
var rawValue: Int
57

68
static let some = MyOptions(rawValue: 4)
@@ -10,6 +12,7 @@ struct SomeOptions: OptionSet {
1012
let someVal = MyOptions(rawValue: 6)
1113
let option = MyOptions(float: Float.infinity)
1214
let none = SomeOptions(rawValue: 0) // expected-error {{value type 'SomeOptions' cannot have a stored property that recursively contains it}}
15+
// expected-note@-1 3{{through reference here}}
1316
}
1417

1518
struct MyOptions: OptionSet {

0 commit comments

Comments
 (0)