@@ -177,21 +177,28 @@ pub fn select(input: TokenStream) -> TokenStream {
177
177
// This prevents creating redundant stack space
178
178
// for them.
179
179
// Passing Futures by path requires those Futures to implement Unpin.
180
+ // We check for this condition here in order to be able to
181
+ // safely use Pin::new_unchecked(&mut #path) later on.
182
+ future_let_bindings. push ( quote ! {
183
+ #futures_crate:: async_await:: assert_fused_future( & mut #path) ;
184
+ #futures_crate:: async_await:: assert_unpin( & mut #path) ;
185
+ } ) ;
180
186
path
181
187
} ,
182
188
_ => {
183
189
// Bind and pin the resulting Future on the stack. This is
184
190
// necessary to support direct select! calls on !Unpin
185
- // Futures.
191
+ // Futures. The Future is not explicitly pinned here with
192
+ // a Pin call, but assumed as pinned. The actual Pin is
193
+ // created inside the poll() function below to defer the
194
+ // creation of the temporary pointer, which would otherwise
195
+ // increase the size of the generated Future.
186
196
// Safety: This is safe since the lifetime of the Future
187
197
// is totally constraint to the lifetime of the select!
188
198
// expression, and the Future can't get moved inside it
189
199
// (it is shadowed).
190
200
future_let_bindings. push ( quote ! {
191
201
let mut #variant_name = #expr;
192
- let mut #variant_name = unsafe {
193
- :: core:: pin:: Pin :: new_unchecked( & mut #variant_name)
194
- } ;
195
202
} ) ;
196
203
parse_quote ! { #variant_name }
197
204
}
@@ -203,8 +210,19 @@ pub fn select(input: TokenStream) -> TokenStream {
203
210
// to use for polling that individual future. These will then be put in an array.
204
211
let poll_functions = bound_future_names. iter ( ) . zip ( variant_names. iter ( ) )
205
212
. map ( |( bound_future_name, variant_name) | {
213
+ // Below we lazily create the Pin on the Future below.
214
+ // This is done in order to avoid allocating memory in the generator
215
+ // for the Pin variable.
216
+ // Safety: This is safe because one of the following condition applies:
217
+ // 1. The Future is passed by the caller by name, and we assert that
218
+ // it implements Unpin.
219
+ // 2. The Future is created in scope of the select! function and will
220
+ // not be moved for the duration of it. It is thereby stack-pinned
206
221
quote ! {
207
222
let mut #variant_name = |__cx: & mut #futures_crate:: task:: Context <' _>| {
223
+ let mut #bound_future_name = unsafe {
224
+ :: core:: pin:: Pin :: new_unchecked( & mut #bound_future_name)
225
+ } ;
208
226
if #futures_crate:: future:: FusedFuture :: is_terminated( & #bound_future_name) {
209
227
None
210
228
} else {
0 commit comments