Files
swift-mirror/test/Concurrency/actor_existentials.swift
Kavon Farvardin e10a4411b6 prevent nonisolated mutation of isolated properties through an existential
The underlying problem was that the ActorIsolationChecker was not properly
recording the mutation environment in which a lookup through an existential was
happening. That's because the AST looks like this:

```
(inout_expr ...
  (open_existential_expr ...
    (opaque_value_expr ...
     (declref_expr ...
     (member_ref_expr ...
       (opaque_value_expr ...
```

and the walker creates a link from the `open_existential_expr` to the `inout_expr`,
but that kind of expression is not checked for its usage environment. Instead, we
need to look through that OpenExistentialExpr recursively so that a link from
its sub-expression, the `member_ref_expr`, to the `inout_expr` is formed instead.

The side-effect of that missing link causing the bug is that the `usageEnv` of
that `member_ref_expr` appears to be empty when we don't have the link. A missing
link is assumed to mean that it's not in a mutating environment, and thus the
usage is treated as a read.

This is why in #59573 / rdar://95509917 we are seeing the compiler report that an
`await` is needed around the assignment expr. The checker thinks it's a read, but
it's actually a mutation, because the member ref is happening in an `inout` expr.

Resolves rdar://95509917
Resolves #59573
2022-06-22 16:03:54 -07:00

70 lines
2.2 KiB
Swift

// RUN: %target-typecheck-verify-swift -disable-availability-checking
// REQUIRES: concurrency
protocol P: Actor {
func f()
var prop: Int { get set } // expected-note 2 {{mutation of this property is only permitted within the actor}}
}
actor A: P {
var prop: Int = 0 // expected-note 2 {{mutation of this property is only permitted within the actor}}
func f() {}
}
func from_isolated_existential1(_ x: isolated any P) {
x.f()
x.prop += 1
x.prop = 100
}
func from_isolated_existential2(_ x: isolated any P) async {
x.f()
x.prop += 1
x.prop = 100
}
func from_nonisolated(_ x: any P) async {
await x.f()
x.prop += 1 // expected-error {{actor-isolated property 'prop' can not be mutated from a non-isolated context}}
x.prop = 100 // expected-error {{actor-isolated property 'prop' can not be mutated from a non-isolated context}}
}
func from_concrete(_ x: A) async {
x.prop += 1 // expected-error {{actor-isolated property 'prop' can not be mutated from a non-isolated context}}
x.prop = 100 // expected-error {{actor-isolated property 'prop' can not be mutated from a non-isolated context}}
}
func from_isolated_concrete(_ x: isolated A) async {
x.prop += 1
x.prop = 100
}
// from https://github.com/apple/swift/issues/59573
actor Act {
var i = 0 // expected-note {{mutation of this property is only permitted within the actor}}
}
let act = Act()
func bad() async {
// expected-warning@+2 {{no 'async' operations occur within 'await' expression}}
// expected-error@+1 {{actor-isolated property 'i' can not be mutated from a non-isolated context}}
await act.i = 666
}
protocol Proto: Actor {
var i: Int { get set } // expected-note 2 {{mutation of this property is only permitted within the actor}}
}
extension Act: Proto {}
func good() async {
// expected-warning@+2 {{no 'async' operations occur within 'await' expression}}
// expected-error@+1 {{actor-isolated property 'i' can not be mutated from a non-isolated context}}
await (act as any Proto).i = 42
let aIndirect: any Proto = act
// expected-warning@+2 {{no 'async' operations occur within 'await' expression}}
// expected-error@+1 {{actor-isolated property 'i' can not be mutated from a non-isolated context}}
await aIndirect.i = 777
}