Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@ -1544,9 +1544,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
103 changes: 70 additions & 33 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,45 +349,82 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
// initializers apply Sendable checking to arguments at the call-site,
// 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;
bool hasError = false;

// FIXME: Calling `getAllMembers` here causes issues for conformance
// synthesis to RawRepresentable and friends. Instead, iterate over
// both the stored properties and the init accessor properties, as
// those can participate in implicit initializers.

auto stored = decl->getStoredProperties();
auto initAccessor = decl->getInitAccessorProperties();

auto shouldAddNonisolated = [&](ArrayRef<VarDecl *> properties) {
if (hasError)
return false;

bool addNonisolated = true;
for (auto *var : properties) {
auto *pbd = var->getParentPatternBinding();
if (!pbd)
continue;

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();
}

// 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()) {
auto type = var->getTypeInContext();
auto isolation = getActorIsolation(var);
if (isolation.isGlobalActor()) {
if (!isSendableType(decl->getModuleContext(), type) ||
var->getInitializerIsolation().isGlobalActor()) {
// If different isolated stored properties require different
// global actors, it is impossible to initialize this type.
if (existingIsolation &&
*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);
var->diagnose(
diag::property_requires_actor,
var->getDescriptiveKind(),
var->getName(), isolation);
}
auto type = var->getTypeInContext();
auto isolation = getActorIsolation(var);
if (isolation.isGlobalActor()) {
if (!isSendableType(decl->getModuleContext(), type) ||
initIsolation.isGlobalActor()) {
// If different isolated stored properties require different
// global actors, it is impossible to initialize this type.
if (existingIsolation != isolation) {
ctx.Diags.diagnose(decl->getLoc(),
diag::conflicting_stored_property_isolation,
ICK == ImplicitConstructorKind::Memberwise,
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);
hasError = true;
return false;
}

existingIsolation = isolation;
previousVar = var;
shouldAddNonisolated = false;
existingIsolation = isolation;
previousVar = var;
addNonisolated = false;
}
}
}
}

if (shouldAddNonisolated) {
return addNonisolated;
};

if (shouldAddNonisolated(stored) &&
shouldAddNonisolated(initAccessor)) {
addNonIsolatedToSynthesized(decl, ctor);
}
}
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 @@ -558,9 +558,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-sns-warning {{global actor 'OtherGlobalActor'-isolated default value in a main actor-isolated context; this is an error in Swift 6}}
// expected-complete-sns-warning@-1 {{memberwise initializer for 'HasWrapperOnUnsafeActor' cannot be both nonisolated and global actor 'OtherGlobalActor'-isolated; this is an error in Swift 6}}
// expected-complete-sns-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-sns-note 2 {{initializer for property '_synced' is global actor 'OtherGlobalActor'-isolated}}
// expected-note @-1 3{{property declared here}}
// expected-complete-sns-note @-2 3{{property declared here}}

Expand All @@ -585,6 +589,10 @@ struct HasWrapperOnUnsafeActor {
}
}

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

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

// expected-complete-sns-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-sns-note@+1 {{initializer for property 'x' is main actor-isolated}}
@MainActor var x = useFooInADefer()
@MainActor var y = { () -> Bool in
var z = statefulThingy
Expand Down
67 changes: 60 additions & 7 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 SendNonSendable %s

Expand Down Expand Up @@ -151,7 +149,7 @@ struct S2 {
}

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

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 @@ -224,8 +220,65 @@ class C3 {
var y = 0
}

@MainActor class MultipleVars {
var (x, y) = (0, 0)
}

func callDefaultInit() async {
_ = C2()
_ = NonIsolatedInit()
_ = NonIsolatedInit(x: 10)
_ = MultipleVars()
}

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

@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-error@+2 {{expression is 'async' but is not marked with 'await'}}
// 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
}
}
}

// Make sure isolation inference for implicit initializers
// doesn't impact conformance synthesis.

struct CError: Error, RawRepresentable {
var rawValue: CInt
}