1111#![ cfg_attr( not( test) , no_std) ]
1212#![ warn( missing_docs) ]
1313
14+ use core:: fmt:: Debug ;
15+ use core:: fmt:: Display ;
1416use core:: future:: Future ;
17+ use core:: future:: IntoFuture ;
1518use core:: marker:: PhantomData ;
1619use core:: mem;
1720use core:: mem:: MaybeUninit ;
@@ -147,34 +150,7 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
147150 #[ allow( clippy:: let_unit_value) ]
148151 let _ = AssertFits :: < F , STACK_SIZE > :: ASSERT ;
149152
150- // Since we have the static assert above, we know this will not fail. This means we could
151- // use `unwrap_unchecked` here. The extra `unsafe` code probably isn't worth it since very
152- // likely the compiler will be able to make that optimization for us, this comment is here
153- // as a reminder that we can change it if for some reason this ends up being a performance
154- // issue in the future.
155- Self :: try_from ( future) . unwrap_or_else ( |f| {
156- match ( Self :: has_alignment_for_val ( & f) , Self :: has_space_for_val ( & f) ) {
157- ( false , false ) => panic ! (
158- "cannot create StackFuture, required size is {}, available space is {}; required alignment is {} but maximum alignment is {}" ,
159- mem:: size_of_val( & f) ,
160- STACK_SIZE ,
161- mem:: align_of:: <F >( ) ,
162- mem:: align_of:: <Self >( )
163- ) ,
164- ( false , true ) => panic ! (
165- "cannot create StackFuture, required alignment is {} but maximum alignment is {}" ,
166- mem:: align_of:: <F >( ) ,
167- mem:: align_of:: <Self >( )
168- ) ,
169- ( true , false ) => panic ! (
170- "cannot create StackFuture, required size is {}, available space is {}" ,
171- mem:: size_of_val( & f) ,
172- STACK_SIZE
173- ) ,
174- // If we have space and alignment, then `try_from` would have succeeded
175- ( true , true ) => unreachable ! ( ) ,
176- }
177- } )
153+ Self :: try_from ( future) . unwrap ( )
178154 }
179155
180156 /// Attempts to create a `StackFuture` from an existing future
@@ -185,7 +161,7 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
185161 /// Panics
186162 ///
187163 /// If we cannot satisfy the alignment requirements for `F`, this function will panic.
188- pub fn try_from < F > ( future : F ) -> Result < Self , F >
164+ pub fn try_from < F > ( future : F ) -> Result < Self , IntoStackFutureError < F > >
189165 where
190166 F : Future < Output = T > + Send + ' a , // the bounds here should match those in the _phantom field
191167 {
@@ -212,7 +188,7 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
212188
213189 Ok ( result)
214190 } else {
215- Err ( future)
191+ Err ( IntoStackFutureError :: new :: < Self > ( future) )
216192 }
217193 }
218194
@@ -231,7 +207,7 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
231207 where
232208 F : Future < Output = T > + Send + ' a , // the bounds here should match those in the _phantom field
233209 {
234- Self :: try_from ( future) . unwrap_or_else ( |future| Self :: from ( Box :: pin ( future) ) )
210+ Self :: try_from ( future) . unwrap_or_else ( |future| Self :: from ( Box :: pin ( future. into_future ( ) ) ) )
235211 }
236212
237213 /// A wrapper around the inner future's poll function, which we store in the poll_fn field
@@ -338,5 +314,106 @@ impl<F, const STACK_SIZE: usize> AssertFits<F, STACK_SIZE> {
338314 } ;
339315}
340316
317+ /// Captures information about why a future could not be converted into a [`StackFuture`]
318+ ///
319+ /// It also contains the original future so that callers can still run the future in error
320+ /// recovery paths, such as by boxing the future instead of wrapping it in [`StackFuture`].
321+ pub struct IntoStackFutureError < F > {
322+ /// The size of the StackFuture we tried to convert the future into
323+ maximum_size : usize ,
324+ /// The StackFuture's alignment
325+ maximum_alignment : usize ,
326+ /// The future that was attempted to be wrapped
327+ future : F ,
328+ }
329+
330+ impl < F > IntoStackFutureError < F > {
331+ fn new < Target > ( future : F ) -> Self {
332+ Self {
333+ maximum_size : mem:: size_of :: < Target > ( ) ,
334+ maximum_alignment : mem:: align_of :: < Target > ( ) ,
335+ future,
336+ }
337+ }
338+
339+ /// Returns true if the target [`StackFuture`] was too small to hold the given future.
340+ pub fn insufficient_space ( & self ) -> bool {
341+ self . maximum_size < mem:: size_of_val ( & self . future )
342+ }
343+
344+ /// Returns true if the target [`StackFuture`]'s alignment was too small to accommodate the given future.
345+ pub fn alignment_too_small ( & self ) -> bool {
346+ self . maximum_alignment < mem:: align_of_val ( & self . future )
347+ }
348+
349+ /// Returns the alignment of the wrapped future.
350+ pub fn required_alignment ( & self ) -> usize {
351+ mem:: align_of_val ( & self . future )
352+ }
353+
354+ /// Returns the size of the wrapped future.
355+ pub fn required_space ( & self ) -> usize {
356+ mem:: size_of_val ( & self . future )
357+ }
358+
359+ /// Returns the alignment of the target [`StackFuture`], which is also the maximum alignment
360+ /// that can be wrapped.
361+ pub const fn available_alignment ( & self ) -> usize {
362+ self . maximum_alignment
363+ }
364+
365+ /// Returns the amount of space that was available in the target [`StackFuture`].
366+ pub const fn available_space ( & self ) -> usize {
367+ self . maximum_size
368+ }
369+ }
370+
371+ impl < F : Future > IntoFuture for IntoStackFutureError < F > {
372+ type Output = F :: Output ;
373+
374+ type IntoFuture = F ;
375+
376+ /// Returns the underlying future that caused this error
377+ ///
378+ /// Can be used to try again, either by directly awaiting the future, wrapping it in a `Box`,
379+ /// or some other method.
380+ fn into_future ( self ) -> Self :: IntoFuture {
381+ self . future
382+ }
383+ }
384+
385+ impl < F > Display for IntoStackFutureError < F > {
386+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
387+ match ( self . alignment_too_small ( ) , self . insufficient_space ( ) ) {
388+ ( true , true ) => write ! ( f,
389+ "cannot create StackFuture, required size is {}, available space is {}; required alignment is {} but maximum alignment is {}" ,
390+ self . required_space( ) ,
391+ self . available_space( ) ,
392+ self . required_alignment( ) ,
393+ self . available_alignment( )
394+ ) ,
395+ ( true , false ) => write ! ( f,
396+ "cannot create StackFuture, required alignment is {} but maximum alignment is {}" ,
397+ self . required_alignment( ) ,
398+ self . available_alignment( )
399+ ) ,
400+ ( false , true ) => write ! ( f,
401+ "cannot create StackFuture, required size is {}, available space is {}" ,
402+ self . required_space( ) ,
403+ self . available_space( )
404+ ) ,
405+ // If we have space and alignment, then `try_from` would have succeeded
406+ ( false , false ) => unreachable ! ( ) ,
407+ }
408+ }
409+ }
410+
411+ impl < F > Debug for IntoStackFutureError < F > {
412+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
413+ // Forward to the Display implementation
414+ write ! ( f, "{self}" )
415+ }
416+ }
417+
341418#[ cfg( test) ]
342419mod tests;
0 commit comments