Skip to content

[Concurrency] Handle cases where a property initializer is subsumed by another property for IsolatedDefaultValues. #71033

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1583,9 +1583,20 @@ void SILGenFunction::emitMemberInitializer(DeclContext *dc, VarDecl *selfDecl,

case ActorIsolation::GlobalActor:
case ActorIsolation::GlobalActorUnsafe:
case ActorIsolation::ActorInstance:
if (requiredIsolation != contextIsolation)
case ActorIsolation::ActorInstance: {
if (requiredIsolation != contextIsolation) {
// Implicit initializers diagnose actor isolation violations
// for property initializers in Sema. Still emit the invalid
// member initializer here to avoid duplicate diagnostics and
// to preserve warn-until-Swift-6 behavior.
auto *init =
dyn_cast_or_null<ConstructorDecl>(dc->getAsDecl());
if (init && init->isImplicit())
break;

continue;
}
}
}

auto *varPattern = field->getPattern(i);
Expand Down
51 changes: 38 additions & 13 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,34 +351,59 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
// and actor initializers do not run on the actor, so initial values
// cannot be actor-instance-isolated.
bool shouldAddNonisolated = true;
llvm::Optional<ActorIsolation> existingIsolation = llvm::None;
ActorIsolation existingIsolation = getActorIsolation(decl);
VarDecl *previousVar = nullptr;

// The memberwise init properties are also effectively what the
// default init uses, e.g. default initializers initialize via
// properties wrapped and init accessors.
for (auto var : decl->getMemberwiseInitProperties()) {
for (auto member : decl->getImplementationContext()->getAllMembers()) {
auto pbd = dyn_cast<PatternBindingDecl>(member);
if (!pbd || pbd->isStatic())
continue;

auto *var = pbd->getSingleVar();
if (!var) {
shouldAddNonisolated = false;
break;
}

auto i = pbd->getPatternEntryIndexForVarDecl(var);
if (pbd->isInitializerSubsumed(i))
continue;

ActorIsolation initIsolation;
if (var->hasInitAccessor()) {
// Init accessors share the actor isolation of the property;
// the accessor body can call anything in that isolation domain,
// and we don't attempt to infer when the isolation isn't
// necessary.
initIsolation = getActorIsolation(var);
} else {
initIsolation = var->getInitializerIsolation();
}

auto type = var->getTypeInContext();
auto isolation = getActorIsolation(var);
if (isolation.isGlobalActor()) {
if (!type->isSendableType() ||
var->getInitializerIsolation().isGlobalActor()) {
initIsolation.isGlobalActor()) {
// If different isolated stored properties require different
// global actors, it is impossible to initialize this type.
if (existingIsolation &&
*existingIsolation != isolation) {
if (existingIsolation != isolation) {
ctx.Diags.diagnose(decl->getLoc(),
diag::conflicting_stored_property_isolation,
ICK == ImplicitConstructorKind::Memberwise,
decl->getDeclaredType(), *existingIsolation, isolation);
previousVar->diagnose(
diag::property_requires_actor,
previousVar->getDescriptiveKind(),
previousVar->getName(), *existingIsolation);
decl->getDeclaredType(), existingIsolation, isolation)
.warnUntilSwiftVersion(6);
if (previousVar) {
previousVar->diagnose(
diag::property_requires_actor,
previousVar->getDescriptiveKind(),
previousVar->getName(), existingIsolation);
}
var->diagnose(
diag::property_requires_actor,
var->getDescriptiveKind(),
var->getName(), isolation);
break;
}

existingIsolation = isolation;
Expand Down
3 changes: 3 additions & 0 deletions test/Concurrency/actor_isolation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,12 @@ func checkIsolationValueType(_ formance: InferredFromConformance,
}

// check for instance members that do not need global-actor protection

// expected-warning@+2 {{memberwise initializer for 'NoGlobalActorValueType' cannot be both nonisolated and global actor 'SomeGlobalActor'-isolated; this is an error in Swift 6}}
// expected-note@+1 2 {{consider making struct 'NoGlobalActorValueType' conform to the 'Sendable' protocol}}
struct NoGlobalActorValueType {
@SomeGlobalActor var point: Point // expected-warning {{stored property 'point' within struct cannot have a global actor; this is an error in Swift 6}}
// expected-note@-1 {{initializer for property 'point' is global actor 'SomeGlobalActor'-isolated}}

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

Expand Down
14 changes: 12 additions & 2 deletions test/Concurrency/global_actor_inference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -559,9 +559,13 @@ struct WrapperOnUnsafeActor<Wrapped> {
}
}

// HasWrapperOnUnsafeActor gets an inferred @MainActor attribute.
// HasWrapperOnUnsafeActor does not have an inferred global actor attribute,
// because synced and $synced have different global actors.
struct HasWrapperOnUnsafeActor {
@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}}
// 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}}
// 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}}

@WrapperOnUnsafeActor var synced: Int = 0 // expected-complete-tns-note 2 {{initializer for property '_synced' is global actor 'OtherGlobalActor'-isolated}}
// expected-note @-1 3{{property declared here}}
// expected-complete-tns-note @-2 3{{property declared here}}

Expand All @@ -586,6 +590,10 @@ struct HasWrapperOnUnsafeActor {
}
}

nonisolated func createHasWrapperOnUnsafeActor() {
_ = HasWrapperOnUnsafeActor()
}

// ----------------------------------------------------------------------
// Nonisolated closures
// ----------------------------------------------------------------------
Expand Down Expand Up @@ -670,7 +678,9 @@ func replacesDynamicOnMainActor() {
// Global-actor isolation of stored property initializer expressions
// ----------------------------------------------------------------------

// expected-complete-tns-warning@+1 {{default initializer for 'Cutter' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
class Cutter {
// expected-complete-tns-note@+1 {{initializer for property 'x' is main actor-isolated}}
@MainActor var x = useFooInADefer()
@MainActor var y = { () -> Bool in
var z = statefulThingy
Expand Down
47 changes: 41 additions & 6 deletions test/Concurrency/isolated_default_arguments.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking

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

Expand Down Expand Up @@ -196,21 +194,19 @@ extension A {
}
}

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

class NonSendable {}

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

Expand All @@ -229,3 +225,42 @@ func callDefaultInit() async {
_ = NonIsolatedInit()
_ = NonIsolatedInit(x: 10)
}

@propertyWrapper
@preconcurrency @MainActor
struct RequiresMain<Value> {
var wrappedValue: Value

init(wrappedValue: Value) {
self.wrappedValue = wrappedValue
}
}

// This is okay; UseRequiresMain has an inferred 'MainActor'
// attribute.
struct UseRequiresMain {
@RequiresMain private var x = 10
}

nonisolated func test() async {
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'; this is an error in Swift 6}}
// expected-note@+1 {{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}}
_ = UseRequiresMain()
}

// expected-warning@+2 {{memberwise initializer for 'InitAccessors' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
// expected-warning@+1 {{default initializer for 'InitAccessors' cannot be both nonisolated and main actor-isolated; this is an error in Swift 6}}
struct InitAccessors {
private var _a: Int

// expected-note@+1 2 {{initializer for property 'a' is main actor-isolated}}
@MainActor var a: Int = 5 {
@storageRestrictions(initializes: _a)
init {
_a = requiresMainActor()
}
get {
_a
}
}
}