Skip to content

Commit a5f0fe7

Browse files
authored
Merge pull request #71033 from hborla/default-init-isolation
[Concurrency] Handle cases where a property initializer is subsumed by another property for `IsolatedDefaultValues`.
2 parents d7fd4ef + a11dc3c commit a5f0fe7

File tree

5 files changed

+158
-44
lines changed

5 files changed

+158
-44
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: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -350,45 +350,82 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
350350
// initializers apply Sendable checking to arguments at the call-site,
351351
// and actor initializers do not run on the actor, so initial values
352352
// cannot be actor-instance-isolated.
353-
bool shouldAddNonisolated = true;
354-
llvm::Optional<ActorIsolation> existingIsolation = llvm::None;
353+
ActorIsolation existingIsolation = getActorIsolation(decl);
355354
VarDecl *previousVar = nullptr;
355+
bool hasError = false;
356+
357+
// FIXME: Calling `getAllMembers` here causes issues for conformance
358+
// synthesis to RawRepresentable and friends. Instead, iterate over
359+
// both the stored properties and the init accessor properties, as
360+
// those can participate in implicit initializers.
361+
362+
auto stored = decl->getStoredProperties();
363+
auto initAccessor = decl->getInitAccessorProperties();
364+
365+
auto shouldAddNonisolated = [&](ArrayRef<VarDecl *> properties) {
366+
if (hasError)
367+
return false;
368+
369+
bool addNonisolated = true;
370+
for (auto *var : properties) {
371+
auto *pbd = var->getParentPatternBinding();
372+
if (!pbd)
373+
continue;
374+
375+
auto i = pbd->getPatternEntryIndexForVarDecl(var);
376+
if (pbd->isInitializerSubsumed(i))
377+
continue;
378+
379+
ActorIsolation initIsolation;
380+
if (var->hasInitAccessor()) {
381+
// Init accessors share the actor isolation of the property;
382+
// the accessor body can call anything in that isolation domain,
383+
// and we don't attempt to infer when the isolation isn't
384+
// necessary.
385+
initIsolation = getActorIsolation(var);
386+
} else {
387+
initIsolation = var->getInitializerIsolation();
388+
}
356389

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()) {
361-
auto type = var->getTypeInContext();
362-
auto isolation = getActorIsolation(var);
363-
if (isolation.isGlobalActor()) {
364-
if (!type->isSendableType() ||
365-
var->getInitializerIsolation().isGlobalActor()) {
366-
// If different isolated stored properties require different
367-
// global actors, it is impossible to initialize this type.
368-
if (existingIsolation &&
369-
*existingIsolation != isolation) {
370-
ctx.Diags.diagnose(decl->getLoc(),
371-
diag::conflicting_stored_property_isolation,
372-
ICK == ImplicitConstructorKind::Memberwise,
373-
decl->getDeclaredType(), *existingIsolation, isolation);
374-
previousVar->diagnose(
375-
diag::property_requires_actor,
376-
previousVar->getDescriptiveKind(),
377-
previousVar->getName(), *existingIsolation);
378-
var->diagnose(
379-
diag::property_requires_actor,
380-
var->getDescriptiveKind(),
381-
var->getName(), isolation);
382-
}
390+
auto type = var->getTypeInContext();
391+
auto isolation = getActorIsolation(var);
392+
if (isolation.isGlobalActor()) {
393+
if (!type->isSendableType() ||
394+
initIsolation.isGlobalActor()) {
395+
// If different isolated stored properties require different
396+
// global actors, it is impossible to initialize this type.
397+
if (existingIsolation != isolation) {
398+
ctx.Diags.diagnose(decl->getLoc(),
399+
diag::conflicting_stored_property_isolation,
400+
ICK == ImplicitConstructorKind::Memberwise,
401+
decl->getDeclaredType(), existingIsolation, isolation)
402+
.warnUntilSwiftVersion(6);
403+
if (previousVar) {
404+
previousVar->diagnose(
405+
diag::property_requires_actor,
406+
previousVar->getDescriptiveKind(),
407+
previousVar->getName(), existingIsolation);
408+
}
409+
var->diagnose(
410+
diag::property_requires_actor,
411+
var->getDescriptiveKind(),
412+
var->getName(), isolation);
413+
hasError = true;
414+
return false;
415+
}
383416

384-
existingIsolation = isolation;
385-
previousVar = var;
386-
shouldAddNonisolated = false;
417+
existingIsolation = isolation;
418+
previousVar = var;
419+
addNonisolated = false;
420+
}
387421
}
388422
}
389-
}
390423

391-
if (shouldAddNonisolated) {
424+
return addNonisolated;
425+
};
426+
427+
if (shouldAddNonisolated(stored) &&
428+
shouldAddNonisolated(initAccessor)) {
392429
addNonIsolatedToSynthesized(decl, ctor);
393430
}
394431
}

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: 60 additions & 7 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

@@ -151,7 +149,7 @@ struct S2 {
151149
}
152150

153151
struct S3 {
154-
// expected-error@+1 {{default argument cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}}
152+
// expected-error@+1 3 {{default argument cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}}
155153
var (x, y, z) = (requiresMainActor(), requiresSomeGlobalActor(), 10)
156154
}
157155

@@ -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

@@ -224,8 +220,65 @@ class C3 {
224220
var y = 0
225221
}
226222

223+
@MainActor class MultipleVars {
224+
var (x, y) = (0, 0)
225+
}
226+
227227
func callDefaultInit() async {
228228
_ = C2()
229229
_ = NonIsolatedInit()
230230
_ = NonIsolatedInit(x: 10)
231+
_ = MultipleVars()
232+
}
233+
234+
// expected-warning@+1 {{default initializer for 'MultipleVarsInvalid' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
235+
class MultipleVarsInvalid {
236+
// expected-note@+1 {{initializer for property 'x' is main actor-isolated}}
237+
@MainActor var (x, y) = (requiresMainActor(), requiresMainActor())
238+
}
239+
240+
@propertyWrapper
241+
@preconcurrency @MainActor
242+
struct RequiresMain<Value> {
243+
var wrappedValue: Value
244+
245+
init(wrappedValue: Value) {
246+
self.wrappedValue = wrappedValue
247+
}
248+
}
249+
250+
// This is okay; UseRequiresMain has an inferred 'MainActor'
251+
// attribute.
252+
struct UseRequiresMain {
253+
@RequiresMain private var x = 10
254+
}
255+
256+
nonisolated func test() async {
257+
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'; this is an error in Swift 6}}
258+
// expected-note@+1 {{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}}
259+
_ = UseRequiresMain()
260+
}
261+
262+
// expected-warning@+2 {{memberwise initializer for 'InitAccessors' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
263+
// expected-warning@+1 {{default initializer for 'InitAccessors' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
264+
struct InitAccessors {
265+
private var _a: Int
266+
267+
// expected-note@+1 2 {{initializer for property 'a' is main actor-isolated}}
268+
@MainActor var a: Int = 5 {
269+
@storageRestrictions(initializes: _a)
270+
init {
271+
_a = requiresMainActor()
272+
}
273+
get {
274+
_a
275+
}
276+
}
277+
}
278+
279+
// Make sure isolation inference for implicit initializers
280+
// doesn't impact conformance synthesis.
281+
282+
struct CError: Error, RawRepresentable {
283+
var rawValue: CInt
231284
}

0 commit comments

Comments
 (0)