Skip to content

Commit 45a89ba

Browse files
committed
Update proposal
1 parent f447a10 commit 45a89ba

File tree

1 file changed

+48
-39
lines changed

1 file changed

+48
-39
lines changed

proposals/nnnn-inferring-senable-for-methods renamed to proposals/nnnn-inferring-senable-for-methods.md

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
# [Pitch] Inferring `@Sendable` for methods
1+
# Inferring `@Sendable` for methods
22

33
* Proposal: [SE-NNNN](https://github.com/kavon/swift-evolution/blob/sendable-functions/proposals/NNNN-filename.md)
44
* Authors: [Angela Laar](https://github.com/angela-laar), [Kavon Farvardin](https://github.com/kavon)
55
* Review Manager: TBD
66
* Status: Awaiting Implementation
7+
* Review: ([pitch](https://forums.swift.org/t/pitch-inferring-sendable-for-methods/66565))
78

89
## Introduction
910

@@ -28,11 +29,11 @@ When referencing a method *without* partially applying it to the object instanc
2829

2930

3031
```
31-
let unapplied:(T) -> (() -> Void) = S.f
32+
let unapplied: (T) -> (() -> Void) = S.f
3233
```
3334

3435

35-
Suppose we want to create a generic method that expects an unapplied function method conforming to Senable as a parameter. We can create a protocol ``P`` that conforms to the `Sendable` protocol and tell our generic function to expect some generic type that conforms to ``P``. We can also use the `@Sendable` attribute, introduced for closures and functions in [SE-302](https://github.com/kavon/swift-evolution/blob/sendable-functions/proposals/0302-concurrent-value-and-concurrent-closures.md), to annotate the closure parameter.
36+
Suppose we want to create a generic method that expects an unapplied function method conforming to Sendable as a parameter. We can create a protocol ``P`` that conforms to the `Sendable` protocol and tell our generic function to expect some generic type that conforms to ``P``. We can also use the `@Sendable` attribute, introduced for closures and functions in [SE-302](https://github.com/kavon/swift-evolution/blob/sendable-functions/proposals/0302-concurrent-value-and-concurrent-closures.md), to annotate the closure parameter.
3637

3738

3839
```
@@ -74,6 +75,8 @@ This is a lot of churn to get the expected behavior. The compiler should preserv
7475

7576
## Proposed solution
7677

78+
We propose the compiler should automatically employ `@Sendable` to functions that cannot capture non-Sendable states. This includes partially-applied and unapplied instance methods of `Sendable` types, as well as non-local functions. Additionally, it should be disallowed to utilize `@Sendable` on instance methods of non-`Sendable` types.
79+
7780
For a function, the `@Sendable` attribute primarily influences the kinds of values that can be captured by the function. But methods of a nominal type do not capture anything but the object instance itself. Semantically, a method can be thought of as being represented by the following functions:
7881

7982

@@ -90,8 +93,9 @@ func NominalType_method_partiallyAppliedTo(_ obj: NominalType) -> ((ArgType) ->
9093
}
9194
return inner
9295
}
96+
// The actual method call
9397
func NominalType_method(_ self: NominalType, _ arg1: ArgType) -> ReturnType {
94-
return self.method(arg1)
98+
/* body of method */
9599
}
96100
```
97101

@@ -104,25 +108,27 @@ type NominalType : Sendable {
104108
}
105109
```
106110

107-
For example, by declaring the following type `Sendable`, the partial and unapplied function values of the type would have implied Sendabilty and the following code would compile with no errors.
108-
111+
For example, by declaring the following type `Sendable`, the partial and unapplied function values of the type would have implied Sendability and the following code would compile with no errors.
109112
```
110113
struct User : Sendable {
111-
func updatePassword (new: String, old:String) -> Bool { /* update password*/ return true}
114+
func updatePassword (new: String, old:String) -> Bool {
115+
/* update password*/
116+
return true
117+
}
112118
}
113119
114-
**let** unapplied: **@Sendable** (User) ((String, String) → Bool) = User.updatePassword // no error
120+
let unapplied: @Sendable (User) -> ((String, String) → Bool) = User.updatePassword // no error
115121
116-
**let** partial: **@Sendable** (String, String) Bool = User().updatePassword // no error
122+
let partial: @Sendable (String, String) -> Bool = User().updatePassword // no error
117123
```
118124

119125

120126

121127
## Detailed design
122128

123-
This proposal includes five changes to `Sendable` behavior.
129+
This proposal includes four changes to `Sendable` behavior.
124130

125-
The first two are what we just discussed regarding partial and unapplied function values.
131+
The first two are what we just discussed regarding partial and unapplied methods.
126132

127133
```
128134
struct User : Sendable {
@@ -136,57 +142,64 @@ struct User : Sendable {
136142
1. The inference of `@Sendable` for unapplied references to methods of a Sendable type.
137143

138144
```
139-
**let** unapplied: **@Sendable** (User) → ((String, String) → Void) = User.changeAddress // no error
145+
let unapplied : @Sendable (User) → ((String, String) → Void) = User.changeAddress // no error
140146
```
141147

142-
1. The inference of `@Sendable` for partially-applied methods of a Sendable type.
148+
2. The inference of `@Sendable` for partially-applied methods of a Sendable type.
143149

144150
```
145-
**let** partial: **@Sendable** (String, String) → Void = User().changeAddress // no error
151+
let partial : @Sendable (String, String) → Void = User().changeAddress // no error
146152
```
147153

148-
The next few are:
154+
These two rules include partially applied and unapplied static methods but do not include partially applied or unapplied mutable methods. Unapplied references to mutable methods are not allowed in the language because they can lead to undefined behavior. More details about this can be found in [SE-0042](https://github.com/apple/swift-evolution/blob/main/proposals/0042-flatten-method-types.md).
155+
156+
Next is:
149157

150-
1. The inference of `@Sendable` when referencing non-local functions.
158+
3. The inference of `@Sendable` when referencing non-local functions.
151159

152-
Unlike closures, which retain the captured value, global functions can't capture any variables - because global variables are just referenced by the function without any ownership. With this in mind there is no reason not to make these, Sendable by default.
160+
Unlike closures, which retain the captured value, global functions can't capture any variables - because global variables are just referenced by the function without any ownership. With this in mind there is no reason not to make these `Sendable` by default. This change will also include static global functions.
153161

154162
```
155163
func doWork() -> Int {
156-
`Int.random(in: 1..<42)`
164+
` Int.random(in: 1..<42)`
157165
}
158166
159167
Task<Int, Never>.detached(priority: **nil**, operation: doWork) // Converting non-sendable function value to '@Sendable () async -> Void' may introduce data races
160168
```
161169

162170
Currently, trying to start a `Task` with the global function `doWork` will cause an error complaining that the function is not `Sendable`. This should compile with no issue.
163171

164-
1. Prohibition of marking methods `@Sendable` when the type they belong to is not `@Sendable`.
165-
1. class C {
166-
var random: Int = 0 // random is mutable so `C` can't be checked sendable
167-
168-
@Sendable func generateN() async -> Int { //error: adding @Sendable to function of non-Senable type prohibited
169-
random = Int.random(in: 1..<100)
170-
return random
171-
}
172-
}
172+
4. Prohibition of marking methods `@Sendable` when the type they belong to is not `@Sendable`.
173+
```
174+
class C {
175+
var random: Int = 0 // random is mutable so `C` can't be checked sendable
173176
174-
Task.detached {
175-
let num = C()
176-
let n = await num.generateN()
177-
num.random = 42 // accessing the `random` var while generateN is mutating it
177+
@Sendable func generateN() async -> Int { //error: adding @Sendable to function of non-Senable type prohibited
178+
random = Int.random(in: 1..<100)
179+
return random
178180
}
179-
2. If we move the previous work we wanted to do into a class that stores the random number we generate as a mutable value, we could be introducing a data race by marking the function responsible for this work `@Sendable` . Doing this should be prohibited by the compiler.
181+
}
182+
183+
func test(c: C) { c.generateN() }
180184
181-
Since `@Sendable` attribute will be automatically determined with this proposal, you don’t have to explicitly write it on function declarations.
185+
let num = C()
186+
Task.detached {
187+
test(num)
188+
}
189+
test(num) // data-race
190+
```
191+
192+
If we move the previous work we wanted to do into a class that stores the random number we generate as a mutable value, we could be introducing a data race by marking the function responsible for this work `@Sendable` . Doing this should be prohibited by the compiler.
193+
194+
Since `@Sendable` attribute will be automatically determined with this proposal, you will no longer have to explicitly write it on function and method declarations.
182195

183196
## Source compatibility
184197

185198
No impact.
186199

187200
## Effect on ABI stability
188201

189-
This would impact the mangling of function names.
202+
When you remove an explicit `@Sendable` from a method, the mangling of that method will change. Since `@Sendable` will now be inferred, if you choose to remove the explicit annotation to "adopt" the inference, you may need to consider the mangling change.
190203

191204
## Effect on API resilience
192205

@@ -201,12 +214,8 @@ Accessors are not currently allowed to participate with the `@Sendable` system i
201214
Swift could forbid explicitly marking function declarations with the` @Sendable` attribute, since under this proposal there’s no longer any reason to do this.
202215

203216
```
204-
/***@Sendable*/** func alwaysSendable() {}
217+
/*@Sendable*/ func alwaysSendable() {}
205218
```
206219

207220
However, since these attributes are allowed today, this would be a source breaking change. Swift 6 could potentially include fix-its to remove `@Sendable` attributes to ease migration, but it’d still be disruptive. The attributes are harmless under this proposal, and they’re still sometimes useful for code that needs to compile with older tools, so we have chosen not to make this change in this proposal. We can consider deprecation at a later time if we find a good reason to do so.
208221

209-
210-
211-
212-

0 commit comments

Comments
 (0)