mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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
70 lines
2.2 KiB
Swift
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
|
|
}
|