Skip to content

Commit 20829fa

Browse files
committed
[Concurrency] Handle cases where a property initializer is subsumed by another
property for IsolatedDefaultValues. For property wrappers and init accesors, skip property initializers that are subsumed, e.g. by an init accessor or a backing property wrapper initializer, and always consider the subsuming initializer to determine whether compiler synthesized initializers should have `nonisolated` applied. This change also lessens the source break of SE-0411 by still emitting member initializers in implicit constructors when the initializer violates actor isolation to preserve the behavior of existing code when concurrency diagnostics are downgraded to warnings in Swift 5 mode.
1 parent 1dec00a commit 20829fa

File tree

5 files changed

+107
-23
lines changed

5 files changed

+107
-23
lines changed

lib/SILGen/SILGenConstructor.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,9 +1583,20 @@ void SILGenFunction::emitMemberInitializer(DeclContext *dc, VarDecl *selfDecl,
15831583

15841584
case ActorIsolation::GlobalActor:
15851585
case ActorIsolation::GlobalActorUnsafe:
1586-
case ActorIsolation::ActorInstance:
1587-
if (requiredIsolation != contextIsolation)
1586+
case ActorIsolation::ActorInstance: {
1587+
if (requiredIsolation != contextIsolation) {
1588+
// Implicit initializers diagnose actor isolation violations
1589+
// for property initializers in Sema. Still emit the invalid
1590+
// member initializer here to avoid duplicate diagnostics and
1591+
// to preserve warn-until-Swift-6 behavior.
1592+
auto *init =
1593+
dyn_cast_or_null<ConstructorDecl>(dc->getAsDecl());
1594+
if (init && init->isImplicit())
1595+
break;
1596+
15881597
continue;
1598+
}
1599+
}
15891600
}
15901601

15911602
auto *varPattern = field->getPattern(i);

lib/Sema/CodeSynthesis.cpp

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -351,34 +351,59 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
351351
// and actor initializers do not run on the actor, so initial values
352352
// cannot be actor-instance-isolated.
353353
bool shouldAddNonisolated = true;
354-
llvm::Optional<ActorIsolation> existingIsolation = llvm::None;
354+
ActorIsolation existingIsolation = getActorIsolation(decl);
355355
VarDecl *previousVar = nullptr;
356356

357-
// The memberwise init properties are also effectively what the
358-
// default init uses, e.g. default initializers initialize via
359-
// properties wrapped and init accessors.
360-
for (auto var : decl->getMemberwiseInitProperties()) {
357+
for (auto member : decl->getImplementationContext()->getAllMembers()) {
358+
auto pbd = dyn_cast<PatternBindingDecl>(member);
359+
if (!pbd || pbd->isStatic())
360+
continue;
361+
362+
auto *var = pbd->getSingleVar();
363+
if (!var) {
364+
shouldAddNonisolated = false;
365+
break;
366+
}
367+
368+
auto i = pbd->getPatternEntryIndexForVarDecl(var);
369+
if (pbd->isInitializerSubsumed(i))
370+
continue;
371+
372+
ActorIsolation initIsolation;
373+
if (var->hasInitAccessor()) {
374+
// Init accessors share the actor isolation of the property;
375+
// the accessor body can call anything in that isolation domain,
376+
// and we don't attempt to infer when the isolation isn't
377+
// necessary.
378+
initIsolation = getActorIsolation(var);
379+
} else {
380+
initIsolation = var->getInitializerIsolation();
381+
}
382+
361383
auto type = var->getTypeInContext();
362384
auto isolation = getActorIsolation(var);
363385
if (isolation.isGlobalActor()) {
364386
if (!type->isSendableType() ||
365-
var->getInitializerIsolation().isGlobalActor()) {
387+
initIsolation.isGlobalActor()) {
366388
// If different isolated stored properties require different
367389
// global actors, it is impossible to initialize this type.
368-
if (existingIsolation &&
369-
*existingIsolation != isolation) {
390+
if (existingIsolation != isolation) {
370391
ctx.Diags.diagnose(decl->getLoc(),
371392
diag::conflicting_stored_property_isolation,
372393
ICK == ImplicitConstructorKind::Memberwise,
373-
decl->getDeclaredType(), *existingIsolation, isolation);
374-
previousVar->diagnose(
375-
diag::property_requires_actor,
376-
previousVar->getDescriptiveKind(),
377-
previousVar->getName(), *existingIsolation);
394+
decl->getDeclaredType(), existingIsolation, isolation)
395+
.warnUntilSwiftVersion(6);
396+
if (previousVar) {
397+
previousVar->diagnose(
398+
diag::property_requires_actor,
399+
previousVar->getDescriptiveKind(),
400+
previousVar->getName(), existingIsolation);
401+
}
378402
var->diagnose(
379403
diag::property_requires_actor,
380404
var->getDescriptiveKind(),
381405
var->getName(), isolation);
406+
break;
382407
}
383408

384409
existingIsolation = isolation;

test/Concurrency/actor_isolation.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,12 @@ func checkIsolationValueType(_ formance: InferredFromConformance,
168168
}
169169

170170
// check for instance members that do not need global-actor protection
171+
172+
// expected-warning@+2 {{memberwise initializer for 'NoGlobalActorValueType' cannot be both nonisolated and global actor 'SomeGlobalActor'-isolated; this is an error in Swift 6}}
171173
// expected-note@+1 2 {{consider making struct 'NoGlobalActorValueType' conform to the 'Sendable' protocol}}
172174
struct NoGlobalActorValueType {
173175
@SomeGlobalActor var point: Point // expected-warning {{stored property 'point' within struct cannot have a global actor; this is an error in Swift 6}}
176+
// expected-note@-1 {{initializer for property 'point' is global actor 'SomeGlobalActor'-isolated}}
174177

175178
@MainActor let counter: Int // expected-warning {{stored property 'counter' within struct cannot have a global actor; this is an error in Swift 6}}
176179

test/Concurrency/global_actor_inference.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -559,9 +559,13 @@ struct WrapperOnUnsafeActor<Wrapped> {
559559
}
560560
}
561561

562-
// HasWrapperOnUnsafeActor gets an inferred @MainActor attribute.
562+
// HasWrapperOnUnsafeActor does not have an inferred global actor attribute,
563+
// because synced and $synced have different global actors.
563564
struct HasWrapperOnUnsafeActor {
564-
@WrapperOnUnsafeActor var synced: Int = 0 // expected-complete-tns-warning {{global actor 'OtherGlobalActor'-isolated default value in a main actor-isolated context; this is an error in Swift 6}}
565+
// expected-complete-tns-warning@-1 {{memberwise initializer for 'HasWrapperOnUnsafeActor' cannot be both nonisolated and global actor 'OtherGlobalActor'-isolated; this is an error in Swift 6}}
566+
// expected-complete-tns-warning@-2 {{default initializer for 'HasWrapperOnUnsafeActor' cannot be both nonisolated and global actor 'OtherGlobalActor'-isolated; this is an error in Swift 6}}
567+
568+
@WrapperOnUnsafeActor var synced: Int = 0 // expected-complete-tns-note 2 {{initializer for property '_synced' is global actor 'OtherGlobalActor'-isolated}}
565569
// expected-note @-1 3{{property declared here}}
566570
// expected-complete-tns-note @-2 3{{property declared here}}
567571

@@ -586,6 +590,10 @@ struct HasWrapperOnUnsafeActor {
586590
}
587591
}
588592

593+
nonisolated func createHasWrapperOnUnsafeActor() {
594+
_ = HasWrapperOnUnsafeActor()
595+
}
596+
589597
// ----------------------------------------------------------------------
590598
// Nonisolated closures
591599
// ----------------------------------------------------------------------
@@ -670,7 +678,9 @@ func replacesDynamicOnMainActor() {
670678
// Global-actor isolation of stored property initializer expressions
671679
// ----------------------------------------------------------------------
672680

681+
// expected-complete-tns-warning@+1 {{default initializer for 'Cutter' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
673682
class Cutter {
683+
// expected-complete-tns-note@+1 {{initializer for property 'x' is main actor-isolated}}
674684
@MainActor var x = useFooInADefer()
675685
@MainActor var y = { () -> Bool in
676686
var z = statefulThingy

test/Concurrency/isolated_default_arguments.swift

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// RUN: %empty-directory(%t)
22

3-
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking
4-
53
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -enable-upcoming-feature IsolatedDefaultValues -parse-as-library -emit-sil -o /dev/null -verify %s
64
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library -emit-sil -o /dev/null -verify -enable-upcoming-feature IsolatedDefaultValues -enable-experimental-feature RegionBasedIsolation %s
75

@@ -196,21 +194,19 @@ extension A {
196194
}
197195
}
198196

199-
// expected-error@+1 {{default initializer for 'C1' cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}}
197+
// expected-warning@+1 {{default initializer for 'C1' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
200198
class C1 {
201199
// expected-note@+1 {{initializer for property 'x' is main actor-isolated}}
202200
@MainActor var x = requiresMainActor()
203-
// expected-note@+1 {{initializer for property 'y' is global actor 'SomeGlobalActor'-isolated}}
204201
@SomeGlobalActor var y = requiresSomeGlobalActor()
205202
}
206203

207204
class NonSendable {}
208205

209-
// expected-error@+1 {{default initializer for 'C2' cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}}
206+
// expected-warning@+1 {{default initializer for 'C2' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
210207
class C2 {
211208
// expected-note@+1 {{initializer for property 'x' is main actor-isolated}}
212209
@MainActor var x = NonSendable()
213-
// expected-note@+1 {{initializer for property 'y' is global actor 'SomeGlobalActor'-isolated}}
214210
@SomeGlobalActor var y = NonSendable()
215211
}
216212

@@ -229,3 +225,42 @@ func callDefaultInit() async {
229225
_ = NonIsolatedInit()
230226
_ = NonIsolatedInit(x: 10)
231227
}
228+
229+
@propertyWrapper
230+
@preconcurrency @MainActor
231+
struct RequiresMain<Value> {
232+
var wrappedValue: Value
233+
234+
init(wrappedValue: Value) {
235+
self.wrappedValue = wrappedValue
236+
}
237+
}
238+
239+
// This is okay; UseRequiresMain has an inferred 'MainActor'
240+
// attribute.
241+
struct UseRequiresMain {
242+
@RequiresMain private var x = 10
243+
}
244+
245+
nonisolated func test() async {
246+
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'; this is an error in Swift 6}}
247+
// expected-note@+1 {{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}}
248+
_ = UseRequiresMain()
249+
}
250+
251+
// expected-warning@+2 {{memberwise initializer for 'InitAccessors' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
252+
// expected-warning@+1 {{default initializer for 'InitAccessors' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
253+
struct InitAccessors {
254+
private var _a: Int
255+
256+
// expected-note@+1 2 {{initializer for property 'a' is main actor-isolated}}
257+
@MainActor var a: Int = 5 {
258+
@storageRestrictions(initializes: _a)
259+
init {
260+
_a = requiresMainActor()
261+
}
262+
get {
263+
_a
264+
}
265+
}
266+
}

0 commit comments

Comments
 (0)