Skip to content

Commit 972e3aa

Browse files
authored
Merge pull request #40908 from kavon/property-initializer-isolation-warning
[SE-327] Reimplement: make initializing expressions for member stored properties nonisolated
2 parents 7264f53 + 6823744 commit 972e3aa

File tree

7 files changed

+118
-14
lines changed

7 files changed

+118
-14
lines changed

include/swift/AST/DeclContext.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ namespace swift {
7373
class PrefixOperatorDecl;
7474
class ProtocolConformance;
7575
class ValueDecl;
76+
class VarDecl;
7677
class Initializer;
7778
class ClassDecl;
7879
class SerializedAbstractClosureExpr;
@@ -355,6 +356,11 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
355356
LLVM_READONLY
356357
ProtocolDecl *getExtendedProtocolDecl() const;
357358

359+
/// If this DeclContext is the initializer expression of a global or instance
360+
/// property, return the VarDecl, otherwise return null.
361+
LLVM_READONLY
362+
VarDecl *getNonLocalVarDecl() const;
363+
358364
/// Retrieve the generic parameter 'Self' from a protocol or
359365
/// protocol extension.
360366
///

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4480,6 +4480,10 @@ ERROR(global_actor_from_nonactor_context,none,
44804480
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
44814481
" from %select{this|a non-isolated}3%select{| synchronous}5 context",
44824482
(DescriptiveDeclKind, DeclName, Type, bool, unsigned, bool))
4483+
ERROR(global_actor_from_initializing_expr,none,
4484+
"expression requiring global actor %0 cannot appear in "
4485+
"default-value expression of %1 %2",
4486+
(Type, DescriptiveDeclKind, DeclName))
44834487
ERROR(actor_isolated_call,none,
44844488
"call to %0 function in a synchronous %1 context",
44854489
(ActorIsolation, ActorIsolation))

lib/AST/Decl.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8792,16 +8792,16 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) {
87928792
// Without this distinction, a nominal can have non-async initializers
87938793
// with various kinds of isolation, so an impossible constraint can be
87948794
// created. See SE-0327 for details.
8795-
if (auto *init = dyn_cast<PatternBindingInitializer>(dc)) {
8796-
if (auto *var =
8797-
init->getBinding()->getAnchoringVarDecl(init->getBindingIndex())) {
8795+
if (auto *var = dc->getNonLocalVarDecl()) {
87988796

8799-
if (var->isInstanceMember() &&
8800-
!var->getAttrs().hasAttribute<LazyAttr>())
8801-
return ActorIsolation::forUnspecified();
8802-
8803-
return getActorIsolation(var);
8797+
// Isolation officially changes, as described above, in Swift 6+
8798+
if (dc->getASTContext().isSwiftVersionAtLeast(6) &&
8799+
var->isInstanceMember() &&
8800+
!var->getAttrs().hasAttribute<LazyAttr>()) {
8801+
return ActorIsolation::forUnspecified();
88048802
}
8803+
8804+
return getActorIsolation(var);
88058805
}
88068806

88078807
if (auto *closure = dyn_cast<AbstractClosureExpr>(dc)) {

lib/AST/DeclContext.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ ProtocolDecl *DeclContext::getExtendedProtocolDecl() const {
8383
return nullptr;
8484
}
8585

86+
VarDecl *DeclContext::getNonLocalVarDecl() const {
87+
if (auto *init = dyn_cast<PatternBindingInitializer>(this)) {
88+
if (auto *var =
89+
init->getBinding()->getAnchoringVarDecl(init->getBindingIndex())) {
90+
return var;
91+
}
92+
}
93+
return nullptr;
94+
}
95+
8696
GenericTypeParamType *DeclContext::getProtocolSelfType() const {
8797
assert(getSelfProtocolDecl() && "not a protocol");
8898

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,57 @@ namespace {
12751275
return getDeclContext()->getParentModule();
12761276
}
12771277

1278+
/// In Swift 6, global-actor isolation is not carried-over to the
1279+
/// initializing expressions of non-static instance properties.
1280+
/// The actual change happens in \c getActorIsolationOfContext ,
1281+
/// but this function exists to warn users of Swift 5 about this
1282+
/// isolation change, so that they can prepare ahead-of-time.
1283+
void warnAboutGlobalActorIsoChangeInSwift6(const ActorIsolation &reqIso,
1284+
const Expr *user) {
1285+
if (ctx.isSwiftVersionAtLeast(6))
1286+
return;
1287+
1288+
// Check our context stack for a PatternBindingInitializer environment.
1289+
DeclContext const* withinDC = nullptr;
1290+
for (auto dc = contextStack.rbegin(); dc != contextStack.rend(); dc++) {
1291+
if (isa<PatternBindingInitializer>(*dc)) {
1292+
withinDC = *dc;
1293+
break;
1294+
}
1295+
}
1296+
1297+
// Not within a relevant decl context.
1298+
if (!withinDC)
1299+
return;
1300+
1301+
// Check if this PatternBindingInitializer's isolation would change
1302+
// in Swift 6+
1303+
if (auto *var = withinDC->getNonLocalVarDecl()) {
1304+
if (var->isInstanceMember() &&
1305+
!var->getAttrs().hasAttribute<LazyAttr>()) {
1306+
// At this point, we know the isolation will change in Swift 6.
1307+
// So, let's check if that change will cause an error.
1308+
1309+
auto dcIso = getActorIsolationOfContext(
1310+
const_cast<DeclContext*>(withinDC));
1311+
1312+
// If the isolation granted in Swift 5 is for a global actor, and
1313+
// the expression requires that global actor's isolation, then it will
1314+
// become an error in Swift 6.
1315+
if (dcIso.isGlobalActor() && dcIso == reqIso) {
1316+
ctx.Diags.diagnose(user->getLoc(),
1317+
diag::global_actor_from_initializing_expr,
1318+
reqIso.getGlobalActor(),
1319+
var->getDescriptiveKind(), var->getName())
1320+
.highlight(user->getSourceRange())
1321+
// make it a warning and attach the "this will become an error..."
1322+
// to the message. The error in Swift 6 will not be this diagnostic.
1323+
.warnUntilSwiftVersion(6);
1324+
}
1325+
}
1326+
}
1327+
}
1328+
12781329
/// Determine whether code in the given use context might execute
12791330
/// concurrently with code in the definition context.
12801331
bool mayExecuteConcurrentlyWith(
@@ -1919,7 +1970,7 @@ namespace {
19191970
// Retrieve the actor isolation of the context.
19201971
switch (auto isolation = getActorIsolationOfContext(dc)) {
19211972
case ActorIsolation::ActorInstance:
1922-
case ActorIsolation::DistributedActorInstance:
1973+
case ActorIsolation::DistributedActorInstance:
19231974
case ActorIsolation::Independent:
19241975
case ActorIsolation::Unspecified:
19251976
return isolation;
@@ -2097,8 +2148,12 @@ namespace {
20972148
// we are within that global actor already.
20982149
Optional<ActorIsolation> unsatisfiedIsolation;
20992150
if (Type globalActor = fnType->getGlobalActor()) {
2100-
if (!getContextIsolation().isGlobalActor() ||
2101-
!getContextIsolation().getGlobalActor()->isEqual(globalActor)) {
2151+
if (getContextIsolation().isGlobalActor() &&
2152+
getContextIsolation().getGlobalActor()->isEqual(globalActor)) {
2153+
warnAboutGlobalActorIsoChangeInSwift6(
2154+
ActorIsolation::forGlobalActor(globalActor, false),
2155+
apply);
2156+
} else {
21022157
unsatisfiedIsolation = ActorIsolation::forGlobalActor(
21032158
globalActor, /*unsafe=*/false);
21042159
}
@@ -2234,6 +2289,8 @@ namespace {
22342289
auto contextIsolation = getInnermostIsolatedContext(declContext);
22352290
if (contextIsolation.isGlobalActor() &&
22362291
contextIsolation.getGlobalActor()->isEqual(globalActor)) {
2292+
2293+
warnAboutGlobalActorIsoChangeInSwift6(contextIsolation, context);
22372294
return false;
22382295
}
22392296

test/Concurrency/global_actor_inference.swift

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -563,10 +563,10 @@ func acceptAsyncSendableClosureInheriting<T>(@_inheritActorContext _: @Sendable
563563

564564
// defer bodies inherit global actor-ness
565565
@MainActor
566-
var statefulThingy: Bool = false
566+
var statefulThingy: Bool = false // expected-note {{var declared here}}
567567

568568
@MainActor
569-
func useFooInADefer() -> String {
569+
func useFooInADefer() -> String { // expected-note {{calls to global function 'useFooInADefer()' from outside of its actor context are implicitly asynchronous}}
570570
defer {
571571
statefulThingy = true
572572
}
@@ -581,3 +581,30 @@ func useFooInADefer() -> String {
581581
func replacesDynamicOnMainActor() {
582582
onlyOnMainActor()
583583
}
584+
585+
// ----------------------------------------------------------------------
586+
// Global-actor isolation of stored property initializer expressions
587+
// ----------------------------------------------------------------------
588+
589+
class Cutter {
590+
@MainActor var x = useFooInADefer() // expected-warning {{expression requiring global actor 'MainActor' cannot appear in default-value expression of property 'x'; this is an error in Swift 6}}
591+
@MainActor var y = { () -> Bool in
592+
var z = statefulThingy // expected-warning {{expression requiring global actor 'MainActor' cannot appear in default-value expression of property 'y'; this is an error in Swift 6}}
593+
return z
594+
}()
595+
}
596+
597+
@SomeGlobalActor
598+
class Butter {
599+
var a = useFooInADefer() // expected-error {{call to main actor-isolated global function 'useFooInADefer()' in a synchronous global actor 'SomeGlobalActor'-isolated context}}
600+
601+
nonisolated let b = statefulThingy // expected-error {{var 'statefulThingy' isolated to global actor 'MainActor' can not be referenced from a non-isolated synchronous context}}
602+
603+
var c: Int = {
604+
return getGlobal7() // expected-warning {{expression requiring global actor 'SomeGlobalActor' cannot appear in default-value expression of property 'c'; this is an error in Swift 6}}
605+
}()
606+
607+
lazy var d: Int = getGlobal7()
608+
609+
static var e: Int = getGlobal7()
610+
}

test/Concurrency/property_initializers.swift renamed to test/Concurrency/property_initializers_swift6.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency
1+
// RUN: %target-typecheck-verify-swift -swift-version 6 -disable-availability-checking -warn-concurrency
22
// REQUIRES: concurrency
33

44

0 commit comments

Comments
 (0)